Page MenuHomePhorge

No OneTemporary

Size
24 KB
Referenced Files
None
Subscribers
None
diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp
index a946c91..63611ec 100644
--- a/examples/type-facilities/type-facilities.cpp
+++ b/examples/type-facilities/type-facilities.cpp
@@ -1,85 +1,85 @@
/*******************************************************************************
*
* File: type-facilities.cpp
*
* Contents: An example showcasing various type-related support facilities.
*
* Copyright 2017
*
* Author: David Juhasz (david.juhasz@tuwien.ac.at)
*
******************************************************************************/
#include "rosa/config/version.h"
#include "rosa/support/log.h"
#include "rosa/support/terminal_colors.h"
#include "rosa/support/type_token.hpp"
#include <iostream>
using namespace rosa;
using namespace rosa::terminal;
int main(void) {
LOG_INFO_STREAM << library_string() << " -- " << Color::Red
<< "type facilities" << Color::Default << std::endl;
auto &Log = LOG_TRACE_STREAM << std::endl;
Log << "NumberOfBuiltinTypes: " << NumberOfBuiltinTypes << std::endl
<< "TokenBits: " << token::TokenBits << std::endl
<< "RepresentationBits: " << token::RepresentationBits << std::endl
<< "MaxTokenizableListSize: " << token::MaxTokenizableListSize
<< std::endl
<< std::endl;
Log << "Type number information on 'uint8_t':" << std::endl;
- constexpr type_nr_t TN = TypeNumber<uint8_t>::Value;
+ constexpr TypeNumber TN = TypeNumberOf<uint8_t>::Value;
Log << " type number: " << PRINTABLE_TN(TN) << std::endl
<< " size: " << TypeForNumber<TN>::Size << std::endl
<< " name: " << TypeForNumber<TN>::Name << std::endl
<< std::endl;
Log << "Type number information of AtomConstants:"
<< std::endl;
using Atom1 = AtomConstant<atom("atom1")>;
using Atom2 = AtomConstant<atom("atom2")>;
Log << " std::is_same<Atom1, Atom2>::value: "
<< std::is_same<Atom1, Atom2>::value << std::endl
- << " TypeNumber<Atom1>::Value: "
- << PRINTABLE_TN(TypeNumber<Atom1>::Value) << std::endl
- << " TypeNumber<Atom2>::Value: "
- << PRINTABLE_TN(TypeNumber<Atom2>::Value) << std::endl
- << " name: " << TypeForNumber<TypeNumber<Atom1>::Value>::Name
+ << " TypeNumberOf<Atom1>::Value: "
+ << PRINTABLE_TN(TypeNumberOf<Atom1>::Value) << std::endl
+ << " TypeNumberOf<Atom2>::Value: "
+ << PRINTABLE_TN(TypeNumberOf<Atom2>::Value) << std::endl
+ << " name: " << TypeForNumber<TypeNumberOf<Atom1>::Value>::Name
<< std::endl
<< std::endl;
Log << "Type token information on 'TypeList<uint8_t, uint16_t>':"
<< std::endl;
// Token is generated statically.
- constexpr token_t Token = TypeToken<uint8_t, uint16_t>::Token;
- STATIC_ASSERT((Token == TypeListToken<TypeList<uint8_t, uint16_t>>::Token),
+ constexpr Token T = TypeToken<uint8_t, uint16_t>::Value;
+ STATIC_ASSERT((T == TypeListToken<TypeList<uint8_t, uint16_t>>::Value),
"alias template definition is wrong");
- token_t T = Token; // We need a non-const value for dropping head later.
+ Token T_ = T; // We need a non-const value for dropping head later.
// Iterate over encoded entries in Token.
- while (!emptyToken(T)) {
- Log << " token: " << PRINTABLE_TOKEN(T) << std::endl
- << " valid: " << validToken(T) << std::endl
- << " empty: " << emptyToken(T) << std::endl
- << " full size: " << sizeOfToken(T) << std::endl
- << " head type number: " << PRINTABLE_TN(typeNumberOfHeadOfToken(T))
+ while (!emptyToken(T_)) {
+ Log << " token: " << PRINTABLE_TOKEN(T_) << std::endl
+ << " valid: " << validToken(T_) << std::endl
+ << " empty: " << emptyToken(T_) << std::endl
+ << " full size: " << sizeOfToken(T_) << std::endl
+ << " head type number: " << PRINTABLE_TN(typeNumberOfHeadOfToken(T_))
<< std::endl
- << " size of head: " << sizeOfHeadOfToken(T) << std::endl
- << " name of head: " << nameOfHeadOfToken(T) << std::endl
- << " is head uint8_t: " << isHeadOfTokenTheSameType<uint8_t>(T)
+ << " size of head: " << sizeOfHeadOfToken(T_) << std::endl
+ << " name of head: " << nameOfHeadOfToken(T_) << std::endl
+ << " is head uint8_t: " << isHeadOfTokenTheSameType<uint8_t>(T_)
<< std::endl
- << " is head uint16_t: " << isHeadOfTokenTheSameType<uint16_t>(T)
+ << " is head uint16_t: " << isHeadOfTokenTheSameType<uint16_t>(T_)
<< std::endl
<< "Dropping head..." << std::endl;
- dropHeadOfToken(T);
+ dropHeadOfToken(T_);
}
// Here when Token became empty.
- Log << " token: " << PRINTABLE_TOKEN(T) << std::endl
- << " empty: " << emptyToken(T) << std::endl;
+ Log << " token: " << PRINTABLE_TOKEN(T_) << std::endl
+ << " empty: " << emptyToken(T_) << std::endl;
return 0;
}
diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp
index f01b5af..ebd7362 100644
--- a/include/rosa/support/type_numbers.hpp
+++ b/include/rosa/support/type_numbers.hpp
@@ -1,155 +1,163 @@
/******************************************************************************
*
* File: type_numbers.hpp
*
* Contents: Facilities for registering supported types and representing them
* with numbers.
*
* Copyright 2017
*
* Author: David Juhasz (david.juhasz@tuwien.ac.at)
*
* This implementation is partially based on the type_number implementation of
* CAF.
* TODO: Check license.
*
******************************************************************************/
#ifndef ROSA_SUPPORT_TYPE_NUMBERS_HPP
#define ROSA_SUPPORT_TYPE_NUMBERS_HPP
#include "rosa/support/atom.hpp"
#include "rosa/support/math.hpp"
#include "rosa/support/squashed_int.hpp"
#include "rosa/support/type_helper.hpp"
#include "rosa/support/types.hpp"
#include <array>
#include <string>
namespace rosa {
// Compile-time list of all built-in types.
// NOTE: Appending new types to the end of this list maintains backward
// compatibility in the sense that old builtin types have the same type number
// associated to them in both the old and new versions. But changing any of
// the already present types in the list breaks that backward compatibility.
// Should compatibility be broken, step TypeNumberVersion below!
// NOTE: Keep this list in sync with the definition of NumberedTypeNames.
using BuiltinTypes = TypeList<AtomValue, // atom
int16_t, // i16
int32_t, // i32
int64_t, // i64
int8_t, // i8
long double, // ldouble
std::string, // str
uint16_t, // u16
uint32_t, // u32
uint64_t, // u64
uint8_t, // u8
unit_t, // unit
bool, // bool
double, // double
float // float
>;
// Indicates the version number of BuiltinTypes. Software with the same version
// number are supposed to have backward compatible type numbering.
// NOTE: See note above on backward compatiblity of BultinTypes.
constexpr size_t TypeNumberVersion = 0;
// The number of built-in types.
static constexpr size_t NumberOfBuiltinTypes =
TypeListSize<BuiltinTypes>::Value;
// Anonymous namespace for helper facilities, consider it private.
namespace {
// Tells if T is not UnitType.
template <typename T> struct IsNotUnitType {
static constexpr bool Value = !std::is_same<T, UnitType>::value;
};
} // End namespace
// Integer type to store type numbers.
// NOTE: The narrowest unsigned integer type that is wide enough to represent
// NumberOfBuiltinTypes different values.
using type_nr_t =
typename TypeListFind<typename TypeListDrop<log2(NumberOfBuiltinTypes) / 8,
IntegerTypesBySize>::Type,
IsNotUnitType>::Type::Second;
+// Turn type_nr_t into a strongly typed enumeration, so TypeNumbers can be used
+// in a type-safe way.
+enum class TypeNumber : type_nr_t {};
+
// A type to cast type numbers into in order to output them to streams as
// numbers and not ASCII-codes.
// NOTE: Use it for safety, necessary for printing uint8_t values.
using printable_tn_t = PRINTABLE(type_nr_t);
// Helper preprocessor macro to cast type numbers into printable_tn_t.
#define PRINTABLE_TN(N) static_cast<printable_tn_t>(N)
// Computes the type number for T.
// NOTE: TypeNumber is the index of T in BuiltinTypes starting from 1,
// index 0 indicates a non-builtin type.
template <typename T, bool IsIntegral = std::is_integral<T>::value>
-struct TypeNumber {
- static constexpr type_nr_t Value =
- static_cast<type_nr_t>(TypeListIndexOf<BuiltinTypes, T>::Value + 1);
+struct TypeNumberOf {
+ static constexpr TypeNumber Value =
+ static_cast<TypeNumber>(TypeListIndexOf<BuiltinTypes, T>::Value + 1);
};
-template <typename T> struct TypeNumber<T, true> {
+template <typename T> struct TypeNumberOf<T, true> {
using Type = squashed_int_t<T>;
- static constexpr type_nr_t Value =
- static_cast<type_nr_t>(TypeListIndexOf<BuiltinTypes, Type>::Value + 1);
+ static constexpr TypeNumber Value =
+ static_cast<TypeNumber>(TypeListIndexOf<BuiltinTypes, Type>::Value + 1);
};
-template <> struct TypeNumber<bool, true> {
- static constexpr type_nr_t Value =
- static_cast<type_nr_t>(TypeListIndexOf<BuiltinTypes, bool>::Value + 1);
+template <> struct TypeNumberOf<bool, true> {
+ static constexpr TypeNumber Value =
+ static_cast<TypeNumber>(TypeListIndexOf<BuiltinTypes, bool>::Value + 1);
};
-template <AtomValue V> struct TypeNumber<AtomConstant<V>, false> {
- static constexpr type_nr_t Value = TypeNumber<AtomValue>::Value;
+template <AtomValue V> struct TypeNumberOf<AtomConstant<V>, false> {
+ static constexpr TypeNumber Value = TypeNumberOf<AtomValue>::Value;
};
// List of all type names, indexed via TypeNumber.
// NOTE: Keep this definition in sync with BuiltinTypes.
constexpr std::array<const char *, NumberOfBuiltinTypes> NumberedTypeNames {{
"atom",
"i16",
"i32",
"i64",
"i8",
"ldouble",
"str",
"u16",
"u32",
"u64",
"u8",
"unit",
"bool",
"double",
"float"
}};
// Tells if the given TypeNumber is valid in the software.
// NOTE: A type number generated by an incompatible version may be valid but
// supposed to denote a type different than that in the current software.
-constexpr bool validTypeNumber(const type_nr_t TypeNumber) {
- return 0 < TypeNumber && TypeNumber <= NumberOfBuiltinTypes;
+constexpr bool validTypeNumber(const TypeNumber TN) {
+ // FIXME: Duplication of static_cast into a const variable would be
+ // possible in C+14.
+ return 0 < static_cast<type_nr_t>(TN) &&
+ static_cast<type_nr_t>(TN) <= NumberOfBuiltinTypes;
}
// Computes the corresponding builtin type with some information from a type
// number.
-// PRE: validTypeNumber(TypeNumber)
-template <type_nr_t TypeNumber> struct TypeForNumber {
- STATIC_ASSERT(validTypeNumber(TypeNumber), "not a valid type number");
- using Type = typename TypeListAt<BuiltinTypes, TypeNumber - 1>::Type;
+// PRE: validTypeNumber(TN)
+template <TypeNumber TN> struct TypeForNumber {
+ STATIC_ASSERT(validTypeNumber(TN), "not a valid type number");
+ static constexpr type_nr_t TNI = static_cast<type_nr_t>(TN);
+ using Type = typename TypeListAt<BuiltinTypes, TNI - 1>::Type;
static constexpr size_t Size = sizeof(Type);
- static constexpr const char *Name = NumberedTypeNames[TypeNumber - 1];
+ static constexpr const char *Name = NumberedTypeNames[TNI - 1];
};
} // End namespace rosa
#endif // ROSA_SUPPORT_TYPE_NUMBERS_HPP
diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp
index ee15d03..b164b86 100644
--- a/include/rosa/support/type_token.hpp
+++ b/include/rosa/support/type_token.hpp
@@ -1,158 +1,164 @@
/******************************************************************************
*
* File: type_token.hpp
*
* Contents: Facilities for encoding TypeLists as unsigned integer values.
*
* Copyright 2017
*
* Author: David Juhasz (david.juhasz@tuwien.ac.at)
*
******************************************************************************/
#ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP
#define ROSA_SUPPORT_TYPE_TOKEN_HPP
#include "rosa/support/type_numbers.hpp"
namespace rosa {
// NOTE on compatibility between different versions of the type token
// implementation:
// Different software versions produce compatible type tokens as long as
// backward compatibility of BuiltinTypes is maintained (denoted by
// TypeNumberVersion, see a note on that) and the type token implementation
// uses the same type as token_t (boiling down to the same token::TokenBits
// value) and the same token::RepresentationBits value. Thus, interacting
// software need to cross-validate the aforementioned values to check
// compatibility. Interoperation between compatible sofware is limited to
// backward compatiblity, that is builtin types defined in both software
// versions are handled correctly but a newer system may produce a type token
// which is invalid in an old one. Therefore, tokens obtained from a compatible
// remote system need to be validated.
// Integer type to store type tokens.
// NOTE: The trade-off between the binary overhead of type encoding and the
// maximal size of encodable lists can be tuned by using unsigned integer types
// of different widths as token_t.
using token_t = uint64_t;
// Sanity check in case someone would change token_t.
STATIC_ASSERT(std::is_unsigned<token_t>::value,
"token_t is not an unsigned integer");
+// Turn token_t into a strongly typed enumeration, so Tokens can be used in a
+// type-safe way.
+enum class Token : token_t {};
+
// A type to cast tokens into in order to output them to streams as
// numbers and not ASCII-codes.
// NOTE: Use it for safety, necessary for printing uint8_t values.
using printable_token_t = PRINTABLE(token_t);
// Helper preprocessor macro to cast tokens into printable_token_t.
#define PRINTABLE_TOKEN(T) static_cast<printable_token_t>(T)
// Nested namespace for protecting constants related to type tokens.
namespace token {
// The number of bits in one token.
-constexpr size_t TokenBits = sizeof(token_t) * 8;
+constexpr size_t TokenBits = sizeof(Token) * 8;
// The number of bits a builtin type can be uniquely encoded into, that is any
// valid TypeNumber can fit into.
// NOTE: There is one extra bit position added for encoding so that providing a
// better chance to maintain backward comaptibility when BuiltinTypes is
// extended.
constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1;
// Maximal size of uniquely tokenizable TypeList.
constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits;
} // End namespace token
// Generates a token, unsigned integer representation, for the TypeList.
// NOTE: The TypeList cannot have more than MaxTokenizableListSize elements and
// must be a subset of BuiltinTypes with respect to squashed integers.
// NOTE: A generated token uniquely represents a list of types, except for
// AtomConstant types. Observe that any AtomConstant is encoded as the type
// AtomValue. The type information on all separate AtomConstant types are lost
// and replaced by the AtomValue type whose actual value needs to be considered
// in order to obtain the original AtomConstant type and so the full type
// information on the encoded TypeList.
template <typename List> struct TypeListTokenImpl;
template <> struct TypeListTokenImpl<EmptyTypeList> {
- static constexpr token_t Token = 0;
+ static constexpr Token Value = static_cast<Token>(0);
};
template <typename T, typename... Ts>
struct TypeListTokenImpl<TypeList<T, Ts...>> {
- static constexpr type_nr_t TN = TypeNumber<T>::Value;
+ static constexpr TypeNumber TN = TypeNumberOf<T>::Value;
// Check if the generated type number is valid.
STATIC_ASSERT(validTypeNumber(TN), "non-builtin type");
- static constexpr token_t Token =
- (TypeListTokenImpl<TypeList<Ts...>>::Token << token::RepresentationBits) |
- TN;
+ static constexpr Token Value = static_cast<Token>(
+ (static_cast<token_t>(TypeListTokenImpl<TypeList<Ts...>>::Value)
+ << token::RepresentationBits) |
+ static_cast<type_nr_t>(TN));
};
template <typename List> struct TypeListToken;
template <typename... Ts> struct TypeListToken<TypeList<Ts...>> {
// NOTE: TypeNumber is computed against squased_int_t for integral types, so
// let's do the same here.
using List = typename SquashedTypeList<TypeList<Ts...>>::Type;
// Check the length of the list here.
// NOTE: Type validation is done one-by-one in TypeListTokenImpl.
STATIC_ASSERT((TypeListSize<List>::Value <= token::MaxTokenizableListSize),
"too long list of types");
- static constexpr token_t Token = TypeListTokenImpl<List>::Token;
+ static constexpr Token Value = TypeListTokenImpl<List>::Value;
};
// Convenience template turning a list of types into a TypeList to generate
// TypeListToken for it.
template <typename... Ts> using TypeToken = TypeListToken<TypeList<Ts...>>;
// Anonymous namespace with helper facilities, consider it private.
namespace {
-// Extracts the type number of the first encoded type of Token.
+// Extracts the type number of the first encoded type of T.
// NOTE: The returned type number is not validated.
-inline type_nr_t typeNumberOfHeadOfToken(const token_t Token) {
- return static_cast<type_nr_t>(Token & ((1 << token::RepresentationBits) - 1));
+inline TypeNumber typeNumberOfHeadOfToken(const Token T) {
+ return static_cast<TypeNumber>(static_cast<token_t>(T) &
+ ((1 << token::RepresentationBits) - 1));
}
} // End namespace
// Tells if the given Token is valid, can be decoded by the current software.
// NOTE: Validation gives a correct result only when Token was generated by a
// compatible software (see note above).
-bool validToken(const token_t Token);
+bool validToken(const Token T);
// Tells if the token does encode an empty list.
-bool emptyToken(const token_t Token);
+bool emptyToken(const Token T);
-// Tells the full memory size of the list encoded in the token.
-// PRE: validToken(Token)
-size_t sizeOfToken(const token_t Token);
+// Tells the full memory size of the list encoded in T.
+// PRE: validToken(T)
+size_t sizeOfToken(const Token T);
-// Tells the memory size of the first type encoded in the token.
-// PRE: !empty(Token) && validToken(Token)
-size_t sizeOfHeadOfToken(const token_t Token);
+// Tells the memory size of the first type encoded in T.
+// PRE: !empty(T) && validToken(T)
+size_t sizeOfHeadOfToken(const Token T);
-// Tells the name of the first type encoded in the token.
-// PRE: !empty(Token) && validToken(Token)
-const char *nameOfHeadOfToken(const token_t Token);
+// Tells the name of the first type encoded in T.
+// PRE: !empty(T) && validToken(T)
+const char *nameOfHeadOfToken(const Token T);
// Drops the head element of the encoded list.
-void dropHeadOfToken(token_t &Token);
-
-// Tells if the head of the encoded list is of type T.
-// PRE: !empty(Token) && validToken(Token)
-template <typename T>
-bool isHeadOfTokenTheSameType(const token_t Token) {
- ASSERT(!emptyToken(Token) && validToken(Token));
- return TypeNumber<T>::Value == typeNumberOfHeadOfToken(Token);
+void dropHeadOfToken(Token &T);
+
+// Tells if the head of the encoded list is of type Type.
+// PRE: !empty(T) && validToken(T)
+template <typename Type>
+bool isHeadOfTokenTheSameType(const Token T) {
+ ASSERT(!emptyToken(T) && validToken(T));
+ return TypeNumberOf<Type>::Value == typeNumberOfHeadOfToken(T);
}
} // End namespace rosa
#endif // ROSA_SUPPORT_TYPE_TOKEN_HPP
diff --git a/lib/support/type_token.cpp b/lib/support/type_token.cpp
index 8fbd0d8..9e8a1aa 100644
--- a/lib/support/type_token.cpp
+++ b/lib/support/type_token.cpp
@@ -1,112 +1,114 @@
/******************************************************************************
*
* File: type_token.cpp
*
* Contents: Runtime part of type token handling.
*
* Copyright 2017
*
* Author: David Juhasz (david.juhasz@tuwien.ac.at)
*
******************************************************************************/
#include "rosa/support/type_token.hpp"
// FIXME: Automatically generate a proper number of cases for the functions
// with a big number of mechanical cases in them below.
namespace rosa {
using namespace token;
-bool validToken(const token_t Token) {
- token_t T = Token;
+bool validToken(const Token T) {
+ Token T_ = T;
bool Valid = true;
- while (Valid && !emptyToken(T)) {
- Valid &= validTypeNumber(typeNumberOfHeadOfToken(T));
- dropHeadOfToken(T);
+ while (Valid && !emptyToken(T_)) {
+ Valid &= validTypeNumber(typeNumberOfHeadOfToken(T_));
+ dropHeadOfToken(T_);
}
return Valid;
}
-bool emptyToken(const token_t Token) { return Token == 0; }
+bool emptyToken(const Token T) { return static_cast<token_t>(T) == 0; }
-size_t sizeOfToken(const token_t Token) {
- ASSERT(validToken(Token));
- token_t T = Token;
+size_t sizeOfToken(const Token T) {
+ ASSERT(validToken(T));
+ Token T_ = T;
size_t S = 0;
- while (!emptyToken(T)) {
- S += sizeOfHeadOfToken(T);
- dropHeadOfToken(T);
+ while (!emptyToken(T_)) {
+ S += sizeOfHeadOfToken(T_);
+ dropHeadOfToken(T_);
}
return S;
}
#define SIZECASE(N) \
{ \
case N: \
- return TypeForNumber<N>::Size; \
+ return TypeForNumber<static_cast<TypeNumber>(N)>::Size; \
}
-size_t sizeOfHeadOfToken(const token_t Token) {
- ASSERT(!emptyToken(Token) && validToken(Token));
- switch (typeNumberOfHeadOfToken(Token)) {
+size_t sizeOfHeadOfToken(const Token T) {
+ ASSERT(!emptyToken(T) && validToken(T));
+ switch (static_cast<type_nr_t>(typeNumberOfHeadOfToken(T))) {
default: {
- // Should never come here when Token is valid and the case-list
- // below covers BuiltinTypes.
+ // Should never come here when T is valid and the case-list below covers
+ // BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
SIZECASE(1);
SIZECASE(2);
SIZECASE(3);
SIZECASE(4);
SIZECASE(5);
SIZECASE(6);
SIZECASE(7);
SIZECASE(8);
SIZECASE(9);
SIZECASE(10);
SIZECASE(11);
SIZECASE(12);
SIZECASE(13);
SIZECASE(14);
SIZECASE(15);
}
}
#define NAMECASE(N) \
{ \
case N: \
- return TypeForNumber<N>::Name; \
+ return TypeForNumber<static_cast<TypeNumber>(N)>::Name; \
}
-const char *nameOfHeadOfToken(const token_t Token) {
- ASSERT(!emptyToken(Token) && validToken(Token));
- switch (typeNumberOfHeadOfToken(Token)) {
+const char *nameOfHeadOfToken(const Token T) {
+ ASSERT(!emptyToken(T) && validToken(T));
+ switch (static_cast<type_nr_t>(typeNumberOfHeadOfToken(T))) {
default: {
- // Should never come here when Token is valid and the case-list
- // below covers BuiltinTypes.
+ // Should never come here when T is valid and the case-list below covers
+ // BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
NAMECASE(1);
NAMECASE(2);
NAMECASE(3);
NAMECASE(4);
NAMECASE(5);
NAMECASE(6);
NAMECASE(7);
NAMECASE(8);
NAMECASE(9);
NAMECASE(10);
NAMECASE(11);
NAMECASE(12);
NAMECASE(13);
NAMECASE(14);
NAMECASE(15);
}
}
-void dropHeadOfToken(token_t &Token) { Token >>= RepresentationBits; }
+void dropHeadOfToken(Token &T) {
+ T = static_cast<Token>(static_cast<token_t>(T) >> RepresentationBits);
+}
} // End namespace rosa

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jul 12, 6:02 PM (18 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
351747
Default Alt Text
(24 KB)

Event Timeline