diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index 7d1269b..640a2d1 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,126 +1,127 @@ //===-- examples/messaging/messaging.cpp ------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/messaging/messaging.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief An example showcasing features related to the \c rosa::Message class. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/MessageHandler.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << '\n'; auto &Log = LOG_INFO_STREAM << '\n'; // Message interface. auto PMsg = Message::create(1, 2); auto &Msg = *PMsg; Log << "Checking on a 'Message with TypeList<>':" << "\n Size: " << Msg.Size << "\n Pos 0 is uint8_t: " << Msg.isTypeAt(0) << "\n Pos 1 is uint16_t: " << Msg.isTypeAt(1) << "\n Pos 2 is uint32_t: " << Msg.isTypeAt(1) << "\n Value at pos 0: " << PRINTABLE(Msg.valueAt(0)) << "\n Value at pos 1: " << Msg.valueAt(1) << "\n\n"; // MessageMatcher. using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyMatcher::doesStronglyMatch(Msg); auto Vs = MyMatcher::extractedValues(Msg); Log << "\n value: '(" << PRINTABLE(std::get<0>(Vs)) << ", " << std::get<1>(Vs) << ")'\n\n"; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n"; 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>':" << "\n Size: " << AMsg.Size << "\n Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << "\n Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << "\n\n"; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << "\n value: '(" - << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" + << std::to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) + << ")'" << "\n\n"; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << "\n\n"; // Invoker. auto IP = Invoker::wrap(Invoker::F([&Log](MyAtom) noexcept->void { Log << "** Handling 'Message with " "TypeList>'.\n"; })); auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(AtomConstant) " "noexcept':" << "\n with 'Message with TypeList'" << "\n does Message match Invoker: " << I.match(Msg) << "\n invoking..."; I(Msg); Log << "\n with 'Message with TypeList>'..." << "\n does Message match Invoker: " << I.match(AMsg) << "\n invoking..."; I(AMsg); Log << "\n\n"; // MessageHandler. MessageHandler Handler{ Invoker::F([&Log](uint8_t, uint16_t) { Log << "** Handling 'Message with TypeList'\n"; }), Invoker::F([&Log](MyAtom) { Log << "** Handling 'Message with " "TypeList>'\n"; })}; auto PANMsg = Message::create(MyNAtom::Value); auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " "{ Invoker::F, " "Invoker::F> }':" << "\n 'Message with TypeList'" << "\n can handle: " << Handler.canHandle(Msg) << "\n handling..."; Handler(Msg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(AMsg) << "\n handling..."; Handler(AMsg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(ANMsg) << "\n handling..."; Handler(ANMsg); Log << "\n\n"; Log << "Terminating, destroying automatic variables.\n"; return 0; } diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index 06b6c60..7a1fb74 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,259 +1,259 @@ //===-- rosa/core/Message.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/Message.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Declaration of \c rosa::Message base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGE_HPP #define ROSA_CORE_MESSAGE_HPP #include "rosa/support/log.h" #include "rosa/support/tokenized_storages.hpp" #include "rosa/core/forward_declarations.h" namespace rosa { /// *Message* interface. /// /// The interface provides means to check the type of the stored values, but /// actual data is to be managed by derived implementations. /// /// A \c rosa::Message instance is an immutable data object that obtains its /// data upon creation and provides only constant references for the stored /// values. /// /// \note Any reference obtained from a \c rosa::Message instance remains valid /// only as long as the owning \c rosa::Message object is not destroyed. /// /// \todo Some member functions of \c rosa::Message duplicate member functions /// of \c rosa::TokenizedStorage, which cannot be easily factored out into a /// common base class due to eventual diamond inheritance issues in derived /// classes. Could this duplication be avoided? class Message { protected: /// Creates a new instance. /// /// \note No implementation for empty list. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \note the actual arguments are ignored by the constructor it is only /// their type that matters. The actual values are supposed to be handled by /// any implementation derived from \c rosa::Message. /// /// \pre \p Type and \p Types are all built-in types and the number of stored /// values does not exceed \c rosa::token::MaxTokenizableListSize. template Message(const Type &, const Types &...) noexcept; /// No copying and moving of \c rosa::Message instances. ///@{ Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; ///@} public: /// Creates a \c rosa::message_t object from constant lvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::message_t object created from the given arguments template static message_t create(const Type &T, const Types &... Ts) noexcept; /// Creates a \c rosa::message_t object from rvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::message_t object created from the given arguments template static message_t create(Type &&T, Types &&... Ts) noexcept; /// Represents the types of the values stored in \p this object. /// /// A valid, non-empty \c rosa::Token representing the types of the values /// stored in \p this object. const Token T; /// The number of values stored in \p this object. /// /// That is the number of types encoded in \c rosa::Message::T. const token_size_t Size; /// Destroys \p this object. virtual ~Message(void); /// Tells if the value stored at a given index is of a given type. /// /// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as /// the \c rosa::AtomValue wrapped into it. /// /// \tparam Type type to match against /// /// \param Pos index the type of the value at is to be matched against \p Type /// /// \return if the value at index \p Pos of type \p Type /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode template bool isTypeAt(const token_size_t Pos) const noexcept; /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam Type type to give a reference of /// /// \param Pos index to set the reference for /// /// \return constant reference of \p Type for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p Type: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode template const Type &valueAt(const token_size_t Pos) const noexcept; protected: /// Provides an untyped pointer for the value at a given index. /// /// \param Pos index to take a pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0; }; /// Nested namespace with implementation for \c rosa::Message, consider it /// private. namespace { /// Template class for an implementation of \c rosa::Message. /// /// \tparam Types types whose values are to be stored template class LocalMessage; /// Implementation of the template \c rosa::LocalMessage providing facilities /// for storing values as a \c rosa::Message object. /// /// \tparam Type type of the first mandatory value of the \c rosa::Message /// \tparam Types of any further values template class LocalMessage final : public Message, private TokenizedStorage { public: /// Creates an instance from constant lvalue references. /// /// \param T the mandatory first value to store in the \c rosa::Message object /// \param Ts optional further values to store in the \c rosa::Message object LocalMessage(const Type &T, const Types &... Ts) noexcept : Message(T, Ts...), TokenizedStorage(T, Ts...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// Creates an instance from rvalue references. /// /// \param T the mandatory first value to store in the \c rosa::Message object /// \param Ts optional further values to store in the \c rosa::Message object LocalMessage(Type &&T, Types &&... Ts) noexcept : Message(T, Ts...), TokenizedStorage(std::move(T), std::move(Ts)...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// Provides an untyped pointer for the constant value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the constant value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < Size); return TokenizedStorage::pointerTo(Pos); } /// Aborts the program! /// /// Since \c rosa::Message instances are supposed to be immutable, the /// non-const inherited function is overridden so that it aborts execution. void *pointerTo(const token_size_t) noexcept override { ROSA_CRITICAL("Unallowed operation of rosa::LocalMessage"); } }; } // End namespace template Message::Message(const Type &, const Types &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check. - LOG_TRACE("Creating Message with Token(" + to_string(T) + ")"); + LOG_TRACE("Creating Message with Token(" + std::to_string(T) + ")"); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template message_t Message::create(Type &&T, Types &&... Ts) noexcept { return message_t( new LocalMessage(std::move(T), std::move(Ts)...)); } template bool Message::isTypeAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size); Token TT = T; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } template const Type &Message::valueAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp index 60fbe06..1dfe3e0 100644 --- a/include/rosa/support/atom.hpp +++ b/include/rosa/support/atom.hpp @@ -1,179 +1,209 @@ //===-- rosa/support/atom.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/atom.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for *atoms*, short strings statically encoded as integers. /// /// \note This implementation is based on the \c atom implementation of CAF. /// \todo Check license. /// /// *Atoms* can be used to turn short string literals into statically generated /// types. The literals may consist of at most \c 10 non-special characters, /// legal characters are \c _0-9A-Za-z and the whitespace character. Special /// characters are turned into whitespace, which may result in different string /// literals being encoded into the same integer value, if any of those contain /// at least one special character. /// /// \note The usage of special characters in the string literals used to create /// *atoms* cannot be checked by the compiler. /// /// Example: /// /// \code /// constexpr AtomValue NameValue = atom("name"); /// using NameAtom = AtomConstant; /// /// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value) /// \endcode /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_ATOM_HPP #define ROSA_SUPPORT_ATOM_HPP #include "rosa/support/debug.hpp" namespace rosa { /// Maximal length of valid atom strings. constexpr size_t MaxAtomLength = 10; /// Underlying integer type of atom values. using atom_t = uint64_t; /// Turn \c rosa::atom_t into a strongly typed enumeration. /// /// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a /// type-safe way. enum class AtomValue : atom_t {}; /// Anonymous namespace with implementational details, consider it private. namespace { // clang-format off /// Encodes ASCII characters to 6-bit encoding. constexpr unsigned char AtomEncodingTable[] = { /* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */ /* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, /* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37, /* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, /* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0}; // clang-format on /// Decodes 6-bit characters to ASCII constexpr char AtomDecodingTable[] = " 0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" "abcdefghijklmnopqrstuvwxyz"; /// Encodes one character and updates the integer representation. /// /// \param Current an encoded value /// \param CharCode a character to add to \p Current /// /// \return \p Current updated with \p CharCode constexpr atom_t nextInterim(atom_t Current, size_t CharCode) { return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0]; } /// Encodes a C-string into an integer value to be used as \c rosa::AtomValue. /// /// \param CStr a string to encode /// \param Interim encoded value to add \p CStr to it /// /// \return \p Interim updated with \p CStr constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) { return (*CStr == '\0') ? Interim : atomValue(CStr + 1, nextInterim(Interim, static_cast(*CStr))); } } // End namespace -/// Converts a \c rosa::AtomValue into \c std::string. -/// -/// \param What value to convert -/// -/// \return \c std::string encoded in \p What -std::string to_string(const AtomValue &What); - /// Converts a \c std::string into a \c rosa::AtomValue. /// /// \param S \c std::string to convert /// /// \return \c rosa::AtomValue representing \p S AtomValue atom_from_string(const std::string &S); /// Converts a string-literal into a \c rosa::AtomValue. /// /// \tparam Size the length of \p Str /// /// \param Str the string-literal to convert /// /// \return \c rosa::AtomValue representating \p Str /// /// \pre \p Str is not too long:\code /// Size <= MaxAtomLength + 1 /// \endcode template constexpr AtomValue atom(char const (&Str)[Size]) { // Last character is the NULL terminator. STATIC_ASSERT(Size <= MaxAtomLength + 1, "Too many characters in atom definition"); return static_cast(atomValue(Str)); } /// Lifts a \c rosa::AtomValue to a compile-time constant. /// /// \tparam V \c rosa::AtomValue to lift template struct AtomConstant { /// Constructor has to do nothing. constexpr AtomConstant(void) {} /// Returns the wrapped value. /// /// \return \p V constexpr operator AtomValue(void) const { return V; } /// Returns the wrapped value as of type \c rosa::atom_t. /// /// \return \c rosa::atom_t value from \p V static constexpr atom_t value() { return static_cast(V); } /// An instance *of this constant* (*not* a \c rosa::AtomValue). static const AtomConstant Value; }; // Implementation of the static member field \c rosa::AtomConstant::Value. template const AtomConstant AtomConstant::Value = AtomConstant{}; +} // End namespace rosa + +namespace std { + +/// Converts a \c rosa::AtomValue into \c std::string. +/// +/// \param What value to convert +/// +/// \return \c std::string encoded in \p What +string to_string(const rosa::AtomValue &What); + +/// Dumps a \c rosa::AtomValue to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param A \c rosa::AtomValue to dump +/// +/// \return \p OS after dumping \p N to it +inline ostream &operator<<(ostream &OS, const rosa::AtomValue &A) { + OS << to_string(A); + return OS; +} + /// Converts a \c rosa::AtomConstant into \c std::string. /// /// \tparam V \c rosa::AtomValue to convert /// /// \note The actual argument of type `const rosa::AtomConstant` is ignored /// because the \c rosa::AtomValue to convert is encoded in the type itself. /// /// \return the original string encoded in \p V -template std::string to_string(const AtomConstant &) { +template string to_string(const rosa::AtomConstant &) { return to_string(V); } -} // End namespace rosa +/// Dumps a \c rosa::AtomConstant to a given \c std::ostream. +/// +/// \tparam V the \c rosa::AtomValue to dump +/// +/// \param [in,out] OS output stream to dump to +/// \param A \c rosa::AtomConstant providing \p V +/// +/// \return \p OS after dumping \p V to it +template +inline ostream &operator<<(ostream &OS, const rosa::AtomConstant &A) { + (void)A; // Shut compiler about unused parameter. + OS << to_string(V); + return OS; +} + +} // End namespace std #endif // ROSA_SUPPORT_ATOM_HPP diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp index 2d5f464..cc85cc7 100644 --- a/include/rosa/support/type_numbers.hpp +++ b/include/rosa/support/type_numbers.hpp @@ -1,225 +1,240 @@ //===-- 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-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 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 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 +namespace std { + +/// Converts a \c rosa::TypeNumber into \c std::string. +/// +/// \param TN \c rosa::TypeNumber to convert +/// +/// \return \c std::string representing \p TN +inline string to_string(const rosa::TypeNumber TN) { + return to_string(static_cast(TN)); +} + +/// Dumps a \c rosa::TypeNumber to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param TN \c rosa::TypeNumber to dump +/// +/// \return \p OS after dumping \p TN to it +inline ostream &operator<<(ostream &OS, const rosa::TypeNumber &TN) { + OS << to_string(TN); + return OS; +} + +} // End namespace std + #endif // ROSA_SUPPORT_TYPE_NUMBERS_HPP diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp index cc4aeaf..99f2cec 100644 --- a/include/rosa/support/type_token.hpp +++ b/include/rosa/support/type_token.hpp @@ -1,306 +1,321 @@ //===-- 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-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 /// Integer type to store length of a \c rosa::Token. /// /// \note The narrowest unsigned integer type that is wide enough to /// represent \c rosa::token::MaxTokenizableListSize different values. using token_size_t = typename TypeListFind< typename TypeListDrop::Type, IsNotNoneType>::Type::Second; /// \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_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 token_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 what type is encoded at Position \p Pos in the \c rosa::Token \p T /// /// \param T \c rosa::Token to check /// \param Pos the position to check /// /// \return \c rosa::TypeNumber of the type encodeded at position \p Pos of \c /// rosa::Token \p T /// /// \pre \p T is valid and it encodes \p Pos types: \code /// validToken(T) && Pos < static_cast(lengthOfToken(T)) /// \endcode inline TypeNumber typeAtPositionOfToken(const Token T, const size_t Pos) { ASSERT(validToken(T) && Pos < static_cast(lengthOfToken(T))); Token TT = T; dropNOfToken(TT, Pos); return headOfToken(TT); } /// 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 +namespace std { + +/// Converts a \c rosa::Token into \c std::string. +/// +/// \param T \c rosa::Token to convert +/// +/// \return \c std::string representing \p T +inline string to_string(const rosa::Token T) { + return to_string(static_cast(T)); +} + +/// Dumps a \c rosa::Token to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param T \c rosa::Token to dump +/// +/// \return \p OS after dumping \p N to it +inline ostream &operator<<(ostream &OS, const rosa::Token &T) { + OS << to_string(T); + return OS; +} + +} // End namespace std + #endif // ROSA_SUPPORT_TYPE_TOKEN_HPP diff --git a/include/rosa/support/types.hpp b/include/rosa/support/types.hpp index e00e04b..eb6a3b2 100644 --- a/include/rosa/support/types.hpp +++ b/include/rosa/support/types.hpp @@ -1,524 +1,550 @@ //===-- rosa/support/types.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/types.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Implementation of some basic convenience types. /// /// \note This implementation is partially based on the implementation of /// corresponding parts of CAF. /// \todo Check license. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPES_HPP #define ROSA_SUPPORT_TYPES_HPP #include "rosa/support/debug.hpp" #include namespace rosa { /* ************************************************************************** * * Unit * * ************************************************************************** */ /// A safe type to replace \c void. /// /// \c rosa::UnitType is analogous to \c void, but can be safely returned, /// stored, etc. to enable higher-order abstraction without cluttering code with /// exceptions for \c void (which can't be stored, for example). struct UnitType { /// Constructor, needs to do nothing. constexpr UnitType() noexcept {} /// Copy-constructor, needs to do nothing. constexpr UnitType(const UnitType &) noexcept {} }; /// Aliasing \c rosa::UnitType as \c rosa::unit_t. using unit_t = UnitType; /// The value of \c rosa::unit_t. /// /// \note Since a value of \c rosa::UnitType has no state, all instances of /// \c rosa::UnitType is equal and considered *the \c rosa::unit_t value*. static constexpr unit_t unit = unit_t{}; // NOLINT -/// Returns the textual representation of any value of \c rosa::unit_t. -/// -/// \return textual representation of \c rosa::UnitType. -inline std::string to_string(const unit_t &) { return "unit"; } - /// \name LiftVoid /// \brief Lifts a type to avoid \c void. /// /// A type \c T can be lifted as \code /// typename LiftVoid::Type /// \endcode /// The resulted type is \c rosa::unit_t if \c T is \c void, and \c T itself /// otherwise. ///@{ /// Definition for the general case. /// /// \tparam T type to lift template struct LiftVoid { using Type = T; }; /// Specialization for \c void. template <> struct LiftVoid { using Type = unit_t; }; ///@} /// \name UnliftVoid /// \brief Unlifts a type already lifted by \c rosa::LiftVoid. /// /// A type \c T can be unlifted as \code /// typename UnliftVoid::Type /// \endcode /// The resulted type is \c void if \c T is \c rosa::unit_t -- that is \c void /// lifted by \c rosa::LiftVoid --, and \c T itself otherwise. /// ///@{ /// Definition for the general case. /// /// \tparam T type to unlift template struct UnliftVoid { using Type = T; }; /// Specialization for \c rosa::unit_t. template <> struct UnliftVoid { using Type = void; }; ///@} /* ************************************************************************** * * None * * ************************************************************************** */ /// Represents *nothing*. /// /// An instance of the type represents *nothing*, that can be used, e.g., for /// clearing an instance of \c rosa::Optional by assigning an instance of /// \c rosa::NoneType to it. struct NoneType { /// Constructor, needs to do nothing. constexpr NoneType(void) {} /// Evaluates the instance to \c bool. /// /// A "nothing" is always evaluates to \c false. constexpr explicit operator bool(void) const { return false; } }; /// Aliasing type \c rosa::NoneType as \c rosa::none_t. using none_t = NoneType; /// The value of \c rosa::none_t. /// /// \note Since a value of \c rosa::NoneType has no state, all instances of /// \c rosa::NoneType is equal and considered *the \c rosa::none_t value*. static constexpr none_t none = none_t{}; // NOLINT -/// Returns the textual representation of any value of \c rosa::none_t. -/// -/// \return textual representation of \c rosa::NoneType. -inline std::string to_string(const none_t &) { return "none"; } - /* ************************************************************************** * * Optional * * ************************************************************************** */ /// \defgroup Optional Specializations of rosa::Optional /// /// \brief Represents an optional value. /// /// \note This implementation is compatible with \c std::optional of C++17. ///@{ /// Definition for the general case, optionally storing a value. /// /// \tparam T type of the optional value template class Optional { public: using Type = T; /// Creates an instance without value. /// /// \note Use it with its default parameter. Optional(const none_t & = none) : Valid(false) {} /// Creates a valid instance with value. /// /// \tparam U type of the \p X /// \tparam E always use it with default value! /// /// \param X value to store in the object /// /// \note The constructor is available for types that are convertible to \p T. template ::value>::type> Optional(U X) : Valid(false) { cr(std::move(X)); } /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to copy Optional(const Optional &Other) : Valid(false) { if (Other.Valid) { cr(Other.Value); } } /// Creates an instance by moving the state of another one. /// /// \param Other the instance whose state to obtain Optional(Optional &&Other) noexcept( std::is_nothrow_move_constructible::value) : Valid(false) { if (Other.Valid) { cr(std::move(Other.Value)); } } /// Destroys \p this object. ~Optional(void) { destroy(); } /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to copy /// /// \return reference of the updated instance Optional &operator=(const Optional &Other) { if (Valid) { if (Other.Valid) { Value = Other.Value; } else { destroy(); } } else if (Other.Valid) { cr(Other.Value); } return *this; } /// Updates \p this object by moving the state of another one. /// /// \param Other the instance whose state to obtain /// /// \return reference of the updated instance Optional &operator=(Optional &&Other) noexcept( std::is_nothrow_destructible::value &&std::is_nothrow_move_assignable::value) { if (Valid) { if (Other.Valid) { Value = std::move(Other.Value); } else { destroy(); } } else if (Other.Valid) { cr(std::move(Other.Value)); } return *this; } /// Checks whether \p this object contains a value. /// /// \return if \p this object contains a value explicit operator bool(void) const { return Valid; } /// Checks whether \p this object does not contain a value. /// /// \return if \p this object does not contain a value bool operator!(void)const { return !Valid; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value T &operator*(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const T &operator*(void)const { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return pointer to the stored value /// /// \pre \p this object contains a value const T *operator->(void)const { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return pointer of the stored value /// /// \pre \p this object contains a value T *operator->(void) { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value T &value(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const T &value(void) const { ASSERT(Valid); return Value; } /// Returns the stored value or a default. /// /// If \p this object contains a value, then the stored value is returned. A /// given default value is returned otherwise. /// /// \param DefaultValue the value to return if \p this object does not contain /// a value /// /// \return reference to either the stored value or \p DefaultValue if \p this /// object does not contain a value const T &valueOr(const T &DefaultValue) const { return Valid ? Value : DefaultValue; } private: /// Deallocates the stored value if any. void destroy(void) { if (Valid) { Value.~T(); Valid = false; } } /// Updates the state of \p this object by moving a value into it. /// /// \tparam V type of \p X /// /// \param X value to move /// /// \pre \p this object does not contain a value template void cr(V &&X) { ASSERT(!Valid); Valid = true; new (&Value) T(std::forward(X)); } /// Denotes if \p this object contains a value. bool Valid; /// Holds the stored value if any. union { T Value; ///< The stored value. }; }; /// Specialization storing a reference. /// /// The specialization allows \p rosa::Optional to hold a reference /// rather than an actual value with minimal overhead. /// /// \tparam T the base type whose reference is to be stored template class Optional { public: using Type = T; /// Creates an instance without reference /// /// \note Use it with its default parameter. Optional(const none_t & = none) : Value(nullptr) {} /// Creates a valid instance with reference. /// /// \param X reference to store in the object Optional(T &X) : Value(&X) {} /// Creates a valid instance with reference. /// /// \param X pointer to store in the object as reference Optional(T *X) : Value(X) {} /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to copy Optional(const Optional &Other) = default; /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to copy /// /// \return reference of the updated instance Optional &operator=(const Optional &Other) = default; /// Checks whether \p this object contains a reference. /// /// \return if \p this object contains a reference explicit operator bool(void) const { return Value != nullptr; } /// Checks whether \p this object does not contain a reference. /// /// \return if \p this object does not contain a reference bool operator!(void)const { return !Value; } /// Returns the reference stored in \p this object. /// /// \return the stored reference /// /// \pre \p this object contains a reference T &operator*(void) { ASSERT(Value); return *Value; } /// Returns the value stored in \p this object. /// /// \return the stored reference /// /// \pre \p this object contains a reference const T &operator*(void)const { ASSERT(Value); return *Value; } /// Returns the value stored in \p this object. /// /// \return the stored reference /// /// \pre \p this object contains a reference T *operator->(void) { ASSERT(Value); return Value; } /// Returns the value stored in \p this object. /// /// \return the stored reference /// /// \pre \p this object contains a reference const T *operator->(void)const { ASSERT(Value); return Value; } /// Returns the value stored in \p this object. /// /// \return the stored reference /// /// \pre \p this object contains a reference T &value(void) { ASSERT(Value); return *Value; } /// Returns the value stored in \p this object. /// /// \return the stored reference /// /// \pre \p this object contains a reference const T &value(void) const { ASSERT(Value); return *Value; } /// Returns the stored reference or a default. /// /// If \p this object contains a reference, then the stored reference is /// returned. A given default value is returned otherwise. /// /// \param DefaultValue the value to return if \p this object does not contain /// a reference /// /// \return either the stored reference or \p DefaultValue if \p this object /// does not contain a reference const T &valueOr(const T &DefaultValue) const { return Value ? Value : DefaultValue; } private: /// The stored reference as a pointer. T *Value; }; /// Specialization storing \c void. /// /// The specialization allows \c rosa::Optional to implement a flag for \c void. template <> class Optional { public: using Type = unit_t; /// Creates an instance with a \c false flag. /// /// \note Use it with its default parameter. Optional(none_t = none) : Value(false) {} /// Creates an instance with a \c true flag. /// /// \note The only argument is ignored because it can be *the \c rosa::unit_t /// value* only. Optional(unit_t) : Value(true) {} /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to copy Optional(const Optional &Other) = default; /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to copy /// /// \return reference of the updated instance Optional &operator=(const Optional &Other) = default; /// Checks whether \p this object contains a \p true flag. /// /// \return if \p this object contains a \p true flag. explicit operator bool(void) const { return Value; } /// Checks whether \p this object contains a \p false flag. /// /// \return if \p this object contains a \p false flag. bool operator!(void)const { return !Value; } private: /// The stored flag. bool Value; }; ///@} } // End namespace rosa +namespace std { + +/// Returns the textual representation of any value of \c rosa::unit_t. +/// +/// \return textual representation of \c rosa::UnitType. +inline std::string to_string(const rosa::unit_t &) { return "unit"; } + +/// Dumps a \c rosa::Unit to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param U \c rosa::Unit to dump +/// +/// \return \p OS after dumping \p U to it +inline ostream &operator<<(ostream &OS, const rosa::unit_t &U) { + OS << to_string(U); + return OS; +} + +/// Returns the textual representation of any value of \c rosa::none_t. +/// +/// \return textual representation of \c rosa::NoneType. +inline std::string to_string(const rosa::none_t &) { return "none"; } + +/// Dumps a \c rosa::none_t to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param N \c rosa::none_t to dump +/// +/// \return \p OS after dumping \p N to it +inline ostream &operator<<(ostream &OS, const rosa::none_t &N) { + OS << to_string(N); + return OS; +} + +} // End namespace std + #endif // ROSA_SUPPORT_TYPES_HPP diff --git a/lib/core/Unit.cpp b/lib/core/Unit.cpp index e693624..007ef55 100644 --- a/lib/core/Unit.cpp +++ b/lib/core/Unit.cpp @@ -1,53 +1,53 @@ //===-- core/Unit.cpp -------------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file core/Unit.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Implementation of rosa/core/Unit.h. /// //===----------------------------------------------------------------------===// #include "rosa/core/Unit.h" #include "rosa/core/System.hpp" // NOLINT #include "rosa/support/debug.hpp" #include "rosa/support/log.h" namespace rosa { Unit::Unit(const AtomValue Kind, const id_t Id, const std::string &Name, System &S) noexcept : Kind(Kind), Id(Id), Name(Name), S(S), FullName(Name + "@" + S.name()) { ASSERT(!Name.empty()); - LOG_TRACE("Constructing Unit '" + FullName + "' of kind '" + to_string(Kind) + - "'"); + LOG_TRACE("Constructing Unit '" + FullName + "' of kind '" + + std::to_string(Kind) + "'"); } Unit::~Unit(void) { LOG_TRACE("Destroying Unit '" + FullName + "'"); } /// The default implementation of \c rosa::Unit::dump emits /// \c rosa::Unit::FullName. std::string Unit::dump(void) const noexcept { LOG_TRACE("Dumping Unit '" + FullName + "'"); return "[Unit] " + FullName; } System &Unit::system(void) const noexcept { return S; } std::ostream &operator<<(std::ostream &OS, const Unit &U) { OS << U.dump(); return OS; } } // End namespace rosa diff --git a/lib/support/atom.cpp b/lib/support/atom.cpp index ee48d3d..9ef7c43 100644 --- a/lib/support/atom.cpp +++ b/lib/support/atom.cpp @@ -1,55 +1,59 @@ //===-- support/atom.cpp ----------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file support/atom.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Implementation of non-static part of atom facilities of /// rosa/support/atom.hpp. /// /// \note This implementation is based on the `atom` implementation of CAF. /// \todo Check license. /// //===----------------------------------------------------------------------===// #include "rosa/support/atom.hpp" #include namespace rosa { -std::string to_string(const AtomValue &What) { - auto X = static_cast(What); - std::string S; - S.reserve(MaxAtomLength + 1); +AtomValue atom_from_string(const std::string &S) { + if (S.size() > MaxAtomLength) { + return atom(""); + } + char AtomBuf[MaxAtomLength + 1]; + std::memcpy(AtomBuf, S.c_str(), S.size()); + AtomBuf[S.size()] = '\0'; + return atom(AtomBuf); +} + +} // End namespace rosa + +namespace std { + +string to_string(const rosa::AtomValue &What) { + auto X = static_cast(What); + string S; + S.reserve(rosa::MaxAtomLength + 1); // Don't read characters before we found the leading 0xF. // First four bits set? bool ReadChars = ((X & 0xF000000000000000) >> 60) == 0xF; uint64_t Mask = 0x0FC0000000000000; for (int BitShift = 54; BitShift >= 0; BitShift -= 6, Mask >>= 6) { if (ReadChars) { - S += AtomDecodingTable[(X & Mask) >> BitShift]; + S += rosa::AtomDecodingTable[(X & Mask) >> BitShift]; } else if (((X & Mask) >> BitShift) == 0xF) { ReadChars = true; } } return S; } -AtomValue atom_from_string(const std::string &S) { - if (S.size() > MaxAtomLength) { - return atom(""); - } - char AtomBuf[MaxAtomLength + 1]; - std::memcpy(AtomBuf, S.c_str(), S.size()); - AtomBuf[S.size()] = '\0'; - return atom(AtomBuf); -} - -} // End namespace rosa +} // End namespace std