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 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::Value; + constexpr TypeNumber TN = TypeNumberOf::Value; Log << " type number: " << PRINTABLE_TN(TN) << std::endl << " size: " << TypeForNumber::Size << std::endl << " name: " << TypeForNumber::Name << std::endl << std::endl; Log << "Type number information of AtomConstants:" << std::endl; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << " std::is_same::value: " << std::is_same::value << std::endl - << " TypeNumber::Value: " - << PRINTABLE_TN(TypeNumber::Value) << std::endl - << " TypeNumber::Value: " - << PRINTABLE_TN(TypeNumber::Value) << std::endl - << " name: " << TypeForNumber::Value>::Name + << " TypeNumberOf::Value: " + << PRINTABLE_TN(TypeNumberOf::Value) << std::endl + << " TypeNumberOf::Value: " + << PRINTABLE_TN(TypeNumberOf::Value) << std::endl + << " name: " << TypeForNumber::Value>::Name << std::endl << std::endl; Log << "Type token information on 'TypeList':" << std::endl; // Token is generated statically. - constexpr token_t Token = TypeToken::Token; - STATIC_ASSERT((Token == TypeListToken>::Token), + constexpr Token T = TypeToken::Value; + STATIC_ASSERT((T == TypeListToken>::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(T) + << " size of head: " << sizeOfHeadOfToken(T_) << std::endl + << " name of head: " << nameOfHeadOfToken(T_) << std::endl + << " is head uint8_t: " << isHeadOfTokenTheSameType(T_) << std::endl - << " is head uint16_t: " << isHeadOfTokenTheSameType(T) + << " is head uint16_t: " << isHeadOfTokenTheSameType(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 #include 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; // 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::Value; // Anonymous namespace for helper facilities, consider it private. namespace { // Tells if T is not UnitType. template struct IsNotUnitType { static constexpr bool Value = !std::is_same::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::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(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 ::value> -struct TypeNumber { - static constexpr type_nr_t Value = - static_cast(TypeListIndexOf::Value + 1); +struct TypeNumberOf { + static constexpr TypeNumber Value = + static_cast(TypeListIndexOf::Value + 1); }; -template struct TypeNumber { +template struct TypeNumberOf { using Type = squashed_int_t; - static constexpr type_nr_t Value = - static_cast(TypeListIndexOf::Value + 1); + static constexpr TypeNumber Value = + static_cast(TypeListIndexOf::Value + 1); }; -template <> struct TypeNumber { - static constexpr type_nr_t Value = - static_cast(TypeListIndexOf::Value + 1); +template <> struct TypeNumberOf { + static constexpr TypeNumber Value = + static_cast(TypeListIndexOf::Value + 1); }; -template struct TypeNumber, false> { - static constexpr type_nr_t Value = TypeNumber::Value; +template struct TypeNumberOf, false> { + static constexpr TypeNumber Value = TypeNumberOf::Value; }; // List of all type names, indexed via TypeNumber. // NOTE: Keep this definition in sync with BuiltinTypes. constexpr std::array 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(TN) && + static_cast(TN) <= NumberOfBuiltinTypes; } // Computes the corresponding builtin type with some information from a type // number. -// PRE: validTypeNumber(TypeNumber) -template struct TypeForNumber { - STATIC_ASSERT(validTypeNumber(TypeNumber), "not a valid type number"); - using Type = typename TypeListAt::Type; +// PRE: validTypeNumber(TN) +template struct TypeForNumber { + STATIC_ASSERT(validTypeNumber(TN), "not a valid type number"); + static constexpr type_nr_t TNI = static_cast(TN); + using Type = typename TypeListAt::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::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(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 struct TypeListTokenImpl; template <> struct TypeListTokenImpl { - static constexpr token_t Token = 0; + static constexpr Token Value = static_cast(0); }; template struct TypeListTokenImpl> { - static constexpr type_nr_t TN = TypeNumber::Value; + static constexpr TypeNumber TN = TypeNumberOf::Value; // Check if the generated type number is valid. STATIC_ASSERT(validTypeNumber(TN), "non-builtin type"); - static constexpr token_t Token = - (TypeListTokenImpl>::Token << token::RepresentationBits) | - TN; + static constexpr Token Value = static_cast( + (static_cast(TypeListTokenImpl>::Value) + << token::RepresentationBits) | + static_cast(TN)); }; template struct TypeListToken; template struct TypeListToken> { // NOTE: TypeNumber is computed against squased_int_t for integral types, so // let's do the same here. using List = typename SquashedTypeList>::Type; // Check the length of the list here. // NOTE: Type validation is done one-by-one in TypeListTokenImpl. STATIC_ASSERT((TypeListSize::Value <= token::MaxTokenizableListSize), "too long list of types"); - static constexpr token_t Token = TypeListTokenImpl::Token; + static constexpr Token Value = TypeListTokenImpl::Value; }; // Convenience template turning a list of types into a TypeList to generate // TypeListToken for it. template using TypeToken = TypeListToken>; // 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(Token & ((1 << token::RepresentationBits) - 1)); +inline TypeNumber typeNumberOfHeadOfToken(const Token T) { + return static_cast(static_cast(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 -bool isHeadOfTokenTheSameType(const token_t Token) { - ASSERT(!emptyToken(Token) && validToken(Token)); - return TypeNumber::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 +bool isHeadOfTokenTheSameType(const Token T) { + ASSERT(!emptyToken(T) && validToken(T)); + return TypeNumberOf::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(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::Size; \ + return TypeForNumber(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(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::Name; \ + return TypeForNumber(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(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(static_cast(T) >> RepresentationBits); +} } // End namespace rosa