diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index 4ba3fb5..c9b11ab 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,143 +1,142 @@ /******************************************************************************* * * File: messaging.cpp * * Contents: An example showcasing features related to Messages. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/config/version.h" #include "rosa/core/MessageHandler.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << std::endl; auto &Log = LOG_INFO_STREAM << std::endl; // Message interface. auto PMsg = Message::create(1, 2); auto &Msg = *PMsg; Log << "Checking on a 'Message with TypeList<>':" << std::endl << " Size: " << Msg.Size << std::endl << " Pos 0 is uint8_t: " << Msg.isTypeAt(0) << std::endl << " Pos 1 is uint16_t: " << Msg.isTypeAt(1) << std::endl << " Pos 2 is uint32_t: " << Msg.isTypeAt(1) << std::endl - << " Value at pos 0: " << PRINTABLE(uint8_t)(Msg.valueAt(0)) - << std::endl + << " Value at pos 0: " << PRINTABLE(Msg.valueAt(0)) << std::endl << " Value at pos 1: " << Msg.valueAt(1) << std::endl << std::endl; // MessageMatcher. using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << std::endl << " matching: " << MyMatcher::doesStronglyMatch(Msg) << std::endl; auto Vs = MyMatcher::extractedValues(Msg); - Log << " value: '(" << PRINTABLE(uint8_t)(std::get<0>(Vs)) << ", " + Log << " value: '(" << PRINTABLE(std::get<0>(Vs)) << ", " << std::get<1>(Vs) << ")'" << std::endl << std::endl; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << std::endl << " matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << std::endl << std::endl; using MyAtom = AtomConstant; const MyAtom &A = MyAtom::Value; using MyNAtom = AtomConstant; auto PAMsg = Message::create(A); auto &AMsg = *PAMsg; Log << "Checking on a 'Message with TypeList>':" << std::endl << " Size: " << AMsg.Size << std::endl << " Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << std::endl << " Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << std::endl << std::endl; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << std::endl << " matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << std::endl << " value: '(" << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" << std::endl << std::endl; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << std::endl << " matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << std::endl << std::endl; // Invoker. auto IP = Invoker::wrap(Invoker::F([&Log](MyAtom) noexcept->void { Log << "** Handling 'Message with TypeList>'." << std::endl; })); auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(AtomConstant) " "noexcept':" << std::endl << " with 'Message with TypeList'" << std::endl << " does Message match Invoker: " << I.match(Msg) << std::endl << " invoking..." << std::endl; I(Msg); Log << " with 'Message with TypeList>'..." << std::endl << " does Message match Invoker: " << I.match(AMsg) << std::endl << " invoking..." << std::endl; I(AMsg); Log << std::endl; // MessageHandler. MessageHandler Handler{ Invoker::F([&Log](uint8_t, uint16_t) { Log << "** Handling 'Message with TypeList'" << std::endl; }), Invoker::F([&Log](MyAtom) { Log << "** Handling 'Message with " "TypeList>'" << std::endl; })}; auto PANMsg = Message::create(MyNAtom::Value); auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " "{ Invoker::F, " "Invoker::F> }':" << std::endl << " 'Message with TypeList'" << std::endl << " can handle: " << Handler.canHandle(Msg) << std::endl << " handling..." << std::endl; Handler(Msg); Log << " 'Message with TypeList>'" << std::endl << " can handle: " << Handler.canHandle(AMsg) << std::endl << " handling..." << std::endl; Handler(AMsg); Log << " 'Message with TypeList>'" << std::endl << " can handle: " << Handler.canHandle(ANMsg) << std::endl << " handling..." << std::endl; Handler(ANMsg); Log << std::endl; Log << "Terminating, destroying automatic variables." << std::endl; return 0; } diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index 48048fe..5b570a1 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,91 +1,91 @@ /******************************************************************************* * * File: debug.hpp * * Contents: Facility for debugging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" #include "rosa/support/type_helper.hpp" #include "rosa/support/terminal_colors.h" #include #include namespace rosa { // Returns an output stream to use for debugging. std::ostream &dbgs(void); // Prints an array to the ostream. -template -std::ostream &operator<<(std::ostream &os, const std::array &arr) { - os << '['; - for (unsigned I = 0; I < size; ++I) { +template +std::ostream &operator<<(std::ostream &OS, const std::array &A) { + OS << '['; + for (unsigned I = 0; I < Size; ++I) { if (I) { - os << ','; + OS << ','; } - os << (PRINTABLE(T)) arr[I]; + OS << PRINTABLE(A[I]); } - os << ']'; - return os; + OS << ']'; + return OS; } } // End namespace rosa #ifndef ROSA_ENABLE_ASSERTIONS #define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ } while (false) #else // defined(NDEBUG) #define DEBUG(X) ROSA_IGNORE_UNUSED(X) #define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) #endif // defined(NDEBUG) // Static assertions are always emitted to the code. #define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/type_helper.hpp b/include/rosa/support/type_helper.hpp index 6f0c794..d2aef8e 100644 --- a/include/rosa/support/type_helper.hpp +++ b/include/rosa/support/type_helper.hpp @@ -1,50 +1,79 @@ /******************************************************************************* * * File: type_helper.hpp * * Contents: Helper facilities for type-related stuff. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_TYPE_HELPER_HPP #define ROSA_SUPPORT_TYPE_HELPER_HPP #include +#include namespace rosa { /* ************************************************************************** * * Printable * * ************************************************************************** */ // A value of type [u]int8_t is treated as a character when being put to an // output stream, which can result in invisible characters being printed. To // avoid that, such a value needs to be casted to a wider type. It can be done // by using the following template to find a target type to cast our value to. -// NOTE: There is a preprocessor macro below which can be used instead of the -// tedious typename stuff. -template +// The template also turns enumerations into their underlying types. Moreover, +// any reference type is turned into the referred type and any non-const type +// is turned into their const-qualified type. +// NOTE: It is important to remove references before checking +// const-qualification because constant references are not const-qualified +// types. +template ::value, + bool IsConst = std::is_const::value, + bool IsEnum = std::is_enum::value> struct PrintableType { using Type = T; }; +template +struct PrintableType { + using Type = + typename PrintableType::type>::Type; +}; + +template +struct PrintableType { + using Type = typename PrintableType::Type; +}; + +template +struct PrintableType { + using Type = + typename PrintableType::type>::Type; +}; + template <> -struct PrintableType { - using Type = unsigned int; +struct PrintableType { + using Type = const unsigned int; }; template <> -struct PrintableType { - using Type = int; +struct PrintableType { + using Type = const int; }; -#define PRINTABLE(T) typename PrintableType::Type +// Convenience template alias for using PrintableType. +template +using printable_t = typename PrintableType::Type; + +// Helper preprocessor macro to cast values to their printable types. +#define PRINTABLE(V) static_cast>(V) } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_HELPER_HPP diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp index 7ae359a..ebc58e3 100644 --- a/include/rosa/support/type_numbers.hpp +++ b/include/rosa/support/type_numbers.hpp @@ -1,162 +1,162 @@ /****************************************************************************** * * 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); +using printable_tn_t = printable_t; // Helper preprocessor macro to cast type numbers into printable_tn_t. #define PRINTABLE_TN(N) static_cast(N) // Converts a TypeNumber into string. inline std::string to_string(const TypeNumber TN) { return std::to_string(static_cast(TN)); } // 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 struct TypeNumberOf { static constexpr TypeNumber Value = static_cast( TypeListIndexOf>::Value + 1); }; template <> struct TypeNumberOf { static constexpr TypeNumber Value = static_cast(TypeListIndexOf::Value + 1); }; template struct TypeNumberOf> { 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 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(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[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 cea2764..9e55b1d 100644 --- a/include/rosa/support/type_token.hpp +++ b/include/rosa/support/type_token.hpp @@ -1,175 +1,175 @@ /****************************************************************************** * * 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); +using printable_token_t = printable_t; // Helper preprocessor macro to cast tokens into printable_token_t. #define PRINTABLE_TOKEN(T) static_cast(T) // Converts a Token into string. inline std::string to_string(const Token T) { return std::to_string(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) * 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 Value = static_cast(0); }; 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)); }; 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 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 T. // NOTE: The returned type number is not validated. 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); // Tells if the token does encode an empty list. bool emptyToken(const Token T); // Tells how many types are encoded in T. size_t lengthOfToken(const Token T); // Tells the full memory size of the list encoded in T. // PRE: validToken(T) size_t sizeOfValuesOfToken(const Token T); // 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 T. // PRE: !empty(T) && validToken(T) const char *nameOfHeadOfToken(const Token T); // Drops the head element of the encoded list. void dropHeadOfToken(Token &T); // Drops the first N element of the encoded list. void dropNOfToken(Token &T, const size_t N); // 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