diff --git a/include/rosa/support/squashed_int.hpp b/include/rosa/support/squashed_int.hpp index 48df20a..2d69d4b 100644 --- a/include/rosa/support/squashed_int.hpp +++ b/include/rosa/support/squashed_int.hpp @@ -1,132 +1,140 @@ //===-- rosa/support/squashed_int.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/squashed_int.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facilities for squashing integer types into standard equivalents. /// /// \note This implementation is partially based on the \c squashed_int /// implementation of CAF. /// \todo Check license. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SQUASHED_INT_HPP #define ROSA_SUPPORT_SQUASHED_INT_HPP #include "rosa/support/type_list.hpp" #include "rosa/support/type_pair.hpp" namespace rosa { /// Compile-time list of integer types. /// /// \note This list is used to select a proper type as \c rosa::type_nr_t, /// always make sure that \c rosa::type_nr_t remains correct whenever changing /// the list. using IntegerTypesBySize = TypeList< // bytes none_t, // 0 TypePair, // 1 TypePair, // 2 none_t, // 3 TypePair, // 4 none_t, // 5 none_t, // 6 none_t, // 7 TypePair // 8 >; -/// Squashes integer types into \c [u]int_[8|16|32|64]_t equivalents. +/// Squashes integral types (except \c bool) into \c [u]int_[8|16|32|64]_t +/// equivalents. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedInt::Type /// \endcode /// -/// \tparam T the integer type to squash +/// \tparam T the integral type to squash /// /// \pre \p T is an integral type:\code /// std::is_integral::value /// \endcode template struct SquashedInt { - STATIC_ASSERT((std::is_integral::value), "squashing a non-integral type"); + STATIC_ASSERT((std::is_integral::value && !std::is_same::value), + "squashing a non-integral type or bool"); using TPair = typename TypeListAt::Type; using Type = typename std::conditional::value, typename TPair::First, typename TPair::Second>::type; }; /// Convenience alias for obtaining a squashed integer type. template using squashed_int_t = typename SquashedInt::Type; /// \defgroup SquashedType Implementation for squashing types /// /// \brief Squashes a type. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedType::Type /// \endcode /// The resulting type is squashed with \c rosa::SquashedInt if \c T is -/// integral, and remains \p T otherwise. +/// integral but not \c bool, and remains \p T otherwise. ///@{ /// Definition for the general case, when squashing a non-integral type. /// /// \tparam T the type to squash /// \tparam IsIntegral Always use the default value! template ::value> struct SquashedType { using Type = T; }; /// Specialization for the case when squashing an integral type. /// /// \tparam T the type to squash template struct SquashedType { using Type = squashed_int_t; }; +/// Specialization for the type \c bool. +/// +/// \note The type \c bool is an integral type and would be squashed by the +/// general case to \c uint8_t without this specialization. +template <> struct SquashedType { using Type = bool; }; + ///@} /// Convenience alias for obtaining a squashed type. template using squashed_t = typename SquashedType::Type; /// \defgroup SquashedTypeList Implementation for squashing lists of types /// /// \brief Squashes a \c rosa::TypeList elementwise. /// /// Replaces all types in a \c rosa::TypeList with their corresponding squashed /// types by using \c rosa::SquashedType. The squashed \c rosa::TypeList /// corresponding to \c List can be obtained as \code /// typename SquashedTypeList::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to squash template struct SquashedTypeList; // Specialization for \c rosa::EmptyTypeList. template <> struct SquashedTypeList { using Type = EmptyTypeList; }; /// Specialization for non-empty \c rosa::TypeList. template struct SquashedTypeList> { using Type = typename TypeListPush< squashed_t, typename SquashedTypeList>::Type>::Type; }; ///@} } // End namespace rosa #endif // ROSA_SUPPORT_SQUASHED_INT_HPP diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp index fc19312..2d5f464 100644 --- a/include/rosa/support/type_numbers.hpp +++ b/include/rosa/support/type_numbers.hpp @@ -1,231 +1,225 @@ //===-- rosa/support/type_numbers.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_numbers.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facilities for registering supported types and representing them with /// numbers. /// /// \note This implementation is partially based on the \c 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 \c rosa::TypeNumberVersion below! /// \note Keep this list in sync with the definition of /// \c rosa::NumberedTypeNames. /// \note The built-in types are explicitly listed in the definition of /// rosa::deluxe::DeluxeAgent. Keep those definitions in sync with this list. using BuiltinTypes = TypeList; /// Indicates the version number of \c rosa::BuiltinTypes. /// /// Software with the same version number are supposed to have backward /// compatible type numbering. /// /// \sa \c rosa::BultinTypes on backward compatibility. 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 a type is not \c rosa::NoneType. /// /// \tparam T the type to check template struct IsNotNoneType { /// Denotes if \p T is the \c rosa::NoneType or not. 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 /// \c NumberOfBuiltinTypes different values. using type_nr_t = typename TypeListFind< typename TypeListDrop::Type, IsNotNoneType>::Type::Second; /// Turn \c rosa::type_nr_t into a strongly typed enumeration. /// /// Values of \c rosa::type_nr_t casted to \c rosa::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 \c uint8_t values. using printable_tn_t = printable_t; /// Casts a \c rosa::TypeNumber into \c rosa::printable_tn_t. /// /// \param TN \c rosa::TypeNumber to cast. #define PRINTABLE_TN(TN) static_cast(TN) /// Converts a \c rosa::TypeNumber into \c std::string. /// /// \param TN \c rosa::TypeNumber to convert /// /// \return \c std::string representing \p TN inline std::string to_string(const TypeNumber TN) { return std::to_string(static_cast(TN)); } /// \name TypeNumberOf /// \brief Computes \c rosa::TypeNumber for a type. /// /// The \c rosa::TypeNumber for a type \c T can be obtained as \code /// TypeNumberOf::Value /// \endcode /// /// \note \c rosa::TypeNumber for a type is based on the corresponding squashed -/// type, except for \c bool and \c rosa::AtomConstant types. +/// type, except for \c rosa::AtomConstant types. /// /// \sa \c rosa::SquashedType /// /// \note \c rosa::TypeNumber is the index of the type in \c rosa::BuiltinTypes /// starting from \c 1; index \c 0 indicates a non-builtin type. ///@{ /// Definition of the template for the general case. /// /// \tparam T type to get \c rosa::TypeNumber for template struct TypeNumberOf { static constexpr TypeNumber Value = static_cast( TypeListIndexOf>::Value + 1); }; -/// Specialization for \c bool. -template <> struct TypeNumberOf { - static constexpr TypeNumber Value = - static_cast(TypeListIndexOf::Value + 1); -}; - /// Specialization for \c rosa::AtomConstant. /// /// \note For a \c rosa::AtomConstant type, \c rosa::TypeNumber is based on the /// \c rosa::AtomValue wrapped into the actual \c rosa::AtomConstant. template struct TypeNumberOf> { static constexpr TypeNumber Value = TypeNumberOf::Value; }; ///@} // clang-format off /// List of type names for all builtin-types, indexed via \c rosa::TypeNumber. /// /// \note Keep this definition in sync with \c rosa::BuiltinTypes. constexpr std::array NumberedTypeNames {{ "atom", "i16", "i32", "i64", "i8", "ldouble", "str", "u16", "u32", "u64", "u8", "unit", "bool", "double", "float" }}; // clang-format on /// Tells if a \c rosa::TypeNumber is valid in the software. /// /// \note A \c rosa::TypeNumber generated by an incompatible version may be /// valid but may denote a type that is different from the \c rosa::TypeNumber /// denotes in the current software. That is why this validation needs to be /// done in connection to checking \c rosa::TypeNumberVersion as well. /// /// \param TN \c rosa::TypeNumber to validate in the context of the current /// software /// /// \return Whether \p TN is valid in the current software constexpr bool validTypeNumber(const TypeNumber TN) { // \todo Duplication of static_cast into a const variable would be // possible in C++14. return 0 < static_cast(TN) && static_cast(TN) <= NumberOfBuiltinTypes; } /// Provides information about the type corresponding to a \c rosa::TypeNumber. /// /// \tparam TN \c rosa::TypeNumber to get information for /// /// \pre Statically, \p TN is a valid \c rosa::TypeNumber: /// \code /// validTypeNumber(TN) /// \endcode template struct TypeForNumber { STATIC_ASSERT(validTypeNumber(TN), "not a valid type number"); /// \p TN as \c rosa::type_nr_t. static constexpr type_nr_t TNI = static_cast(TN); /// The builtin-type corresponding to \p TN. using Type = typename TypeListAt::Type; /// The size of \c Type. static constexpr size_t Size = sizeof(Type); /// Textual representation of the builtin-type. 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 94db36f..ed753bf 100644 --- a/include/rosa/support/type_token.hpp +++ b/include/rosa/support/type_token.hpp @@ -1,279 +1,279 @@ //===-- rosa/support/type_token.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_token.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facilities for encoding TypeLists as unsigned integer values. /// /// \note **On the compatibility between different versions of the type token /// implementation:** /// Different software versions produce compatible type tokens as long as /// *backward compatibility* of \c rosa::BuiltinTypes is maintained (denoted by /// \c rosa::TypeNumberVersion) and the type token implementation uses the same /// type as \c rosa::token_t (boiling down to the same \c rosa::token::TokenBits /// value) and the same \c rosa::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. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP #define ROSA_SUPPORT_TYPE_TOKEN_HPP #include "rosa/support/type_numbers.hpp" namespace rosa { /// 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 \c rosa::token_t. using token_t = uint64_t; /// Sanity check in case someone would change \c rosa::token_t. STATIC_ASSERT(std::is_unsigned::value, "token_t is not an unsigned integer"); /// Turn \c rosa::token_t into a strongly typed enumeration. /// /// Values of \c rosa::token_t casted to \c rosa::Token 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 \c uint8_t values. using printable_token_t = printable_t; /// Casts a \c rosa::Token into \c rosa::printable_token_t. /// /// \param T \c rosa::Token to cast #define PRINTABLE_TOKEN(T) static_cast(T) /// Converts a \c rosa::Token into \c std::string. /// /// \param T \c rosa::Token to convert /// /// \return \c std::string representing \p T inline std::string to_string(const Token T) { return std::to_string(static_cast(T)); } /// Encloses constants related to the implementation of \c rosa::Token. namespace token { /// The number of bits in one \c rosa::Token. constexpr size_t TokenBits = sizeof(Token) * 8; /// The number of bits a builtin type can be uniquely encoded into, that is any /// valid \c rosa::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 \c rosa::BuiltinTypes /// is extended. constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1; /// Maximal size of uniquely tokenizable \c rosa::TypeList. constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits; } // End namespace token /// \defgroup TypeListTokenImpl Implementation of rosa::TypeListToken /// /// \brief Generates a \c rosa::Token for a squashed \c rosa::TypeList. /// /// \note Only to be used by the implementation of \c rosa::TypeListToken. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to generate \c rosa::Token for template struct TypeListTokenImpl; /// Specialization for \c rosa::EmptyTypeList. template <> struct TypeListTokenImpl { static constexpr Token Value = static_cast(0); }; /// Specialization for a non-empty \c rosa::TypeList. template struct TypeListTokenImpl> { static constexpr TypeNumber TN = TypeNumberOf::Value; // Check if the generated type number is valid. STATIC_ASSERT(validTypeNumber(TN), "non-builtin type"); static constexpr Token Value = static_cast( (static_cast(TypeListTokenImpl>::Value) << token::RepresentationBits) | static_cast(TN)); }; ///@} /// \name TypeListToken /// /// \brief Generates a \c rosa::Token for a \c rosa::TypeList. /// /// \c rosa::Token for a \c rosa::TypeList \c List can be obtained as \code /// TypeListToken::Value /// \endcode /// /// \note The \c rosa::TypeList cannot have more than /// \c rosa::token::MaxTokenizableListSize elements and must be a subset of /// \c rosa::BuiltinTypes with respect to squashed integers. /// /// \note A generated \c rosa::Token uniquely represents a list of types, except /// for \c rosa::AtomConstant types. Observe that any \c rosa::AtomConstant is /// encoded as the type \c rosa::AtomValue. The type information on all separate /// \c rosa::AtomConstant types are lost and replaced by the \c rosa::AtomValue /// type whose actual value needs to be used in order to obtain the original /// \c rosa::AtomConstant type and so the full type information on the encoded /// \c rosa::TypeList. /// ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to generate \c rosa::Token for template struct TypeListToken; /// Implementation using \c rosa::TypeListTokenImpl. template struct TypeListToken> { - /// \note \c rosa::TypeNumber is computed against \c rosa::squased_int_t for - /// integral types, so let's do the same here. + /// \note \c rosa::TypeNumber is computed against \c rosa::squased_t, 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 \c rosa::TypeListTokenImpl. STATIC_ASSERT((TypeListSize::Value <= token::MaxTokenizableListSize), "too long list of types"); /// The \c rosa::Token for \p List. static constexpr Token Value = TypeListTokenImpl::Value; }; ///@} /// Convenience template to generate \c rosa::Token for a list of types. template using TypeToken = TypeListToken>; /// Tells if a given \c rosa::Token is valid. /// /// A \c rosa::Token is considered valid if it can be decoded by the current /// software. /// /// \note Validation gives a correct result only when \p T was generated by a /// compatible software. /// /// \sa Note for type_token.hpp on compatibility. /// /// \param T \c rosa::Token to validate /// /// \return if \p T is valid bool validToken(const Token T); /// Tells if a \c rosa::Token does encode an empty list of types. /// /// \param T \c rosa::Token to check /// /// \return if \p T encodes an empty list of types bool emptyToken(const Token T); /// Tells how many types are encoded in a \c rosa::Token. /// /// \param T \c rosa::Token to check /// /// \return how many types are encoded in \p T size_t lengthOfToken(const Token T); /// Tells the full memory size of the types encoded in a \c rosa::Token. /// /// The full memory size of the types encoded in a \c rosa::Token is the sum of /// the separate memory sizes of all the types encoded in the \c rosa::Token. /// /// \param T \c rosa::Token to check /// /// \return full memory size of the types encoded in \p T /// /// \pre \p T is valid:\code /// validToken(T) /// \endcode size_t sizeOfValuesOfToken(const Token T); /// Extracts the \c rosa::TypeNumber of the first encoded type of a /// \c rosa::Token. /// /// \note The returned type number is not validated. /// /// \param T \c rosa::Token to take the first \c rosa::TypeNumber from /// /// \return the first \c rosa::TypeNumber encoded in \p T inline TypeNumber headOfToken(const Token T) { return static_cast(static_cast(T) & ((1 << token::RepresentationBits) - 1)); } /// Tells the memory size of the first type encoded in a \c rosa::Token. /// /// \param T \c rosa::Token to check the first encoded type of /// /// \return memory size of the first type encoded in \p T /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode size_t sizeOfHeadOfToken(const Token T); /// Gives the textual representation of the first type encoded in a /// \c rosa::Token. /// /// \param T \c rosa::Token to take the name of its first encoded type /// /// \return textual representation of the first type encoded in \p T /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode const char *nameOfHeadOfToken(const Token T); /// Updates a \c rosa::Token by dropping its first encoded type. /// /// \param [in,out] T \c rosa::Token to drop the first encoded type of void dropHeadOfToken(Token &T); /// Updates a \c rosa::Token by dropping a number of its first encoded types /// /// \param [in,out] T \c rosa::Token to drop the first \p N encoded types of /// \param N the number of types to drop from \p T void dropNOfToken(Token &T, const size_t N); /// Tells if the first encoded type of a \c rosa::Token is a given type. /// /// \tparam Type type to match the first encoded type of \p T against /// /// \param T \c rosa::Token whose first encoded type is to be matched against /// \p Type /// /// \return if the first encoded type of \p T is \p Type /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode template bool isHeadOfTokenTheSameType(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); return TypeNumberOf::Value == headOfToken(T); } } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_TOKEN_HPP