diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index 4d280b5..cd4f1e6 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,140 +1,140 @@ /******************************************************************************* * * 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.getValueAt(0)) + << " Value at pos 0: " << PRINTABLE(uint8_t)(Msg.valueAt(0)) << std::endl - << " Value at pos 1: " << Msg.getValueAt(1) << 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)) << ", " << 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/core/Message.hpp b/include/rosa/core/Message.hpp index c874a45..02ce324 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,307 +1,306 @@ /******************************************************************************* * * File: Message.hpp * * Contents: Declaration of Message base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGE_HPP #define ROSA_CORE_MESSAGE_HPP #include "rosa/support/log.h" #include "rosa/support/type_token.hpp" #include #include 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. // Messages are immutable data objects, obtaining their data upon creation and // providing only constant references for the stored values. // NOTE: Any reference obtained from a Message instance remains valid only as // long as the owning Message object is not destroyed. class Message { protected: // Ctor. // NOTE: No implementation for empty list. template Message(const Type &, const Ts &...) noexcept; // No copy and move. Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; public: // Type alias for a smart pointer for Message. using message_t = std::unique_ptr; // Factory function to locally create a Message instance from constant // references. template static message_t create(const Type &T, const Types &... Ts) noexcept; // Factory function to locally create a Message instance from movable // references. template static message_t create(Type &&T, Types &&... Ts) noexcept; // A valid, non-empty token representing the types of the values stored in // the Message. const Token T; // The number of types encoded in T, that is the number of values stored in // Message. const size_t Size; // Virtual dtor. virtual ~Message(void); // Tells if the value in position Pos is of type Type. // NOTE: Token encodes atoms as AtomValue and not directly AtomConstants. // PRE: Pos < Size template bool isTypeAt(const size_t Pos) const noexcept; // Gives a constant reference of the value of type Type in position Pos. // PRE: Pos < Size && isTypeAt(Pos) - template - const Type &getValueAt(const size_t Pos) const noexcept; + template const Type &valueAt(const size_t Pos) const noexcept; protected: // Provides an untyped pointer for the value in position Pos. // PRE: Pos < Size - virtual const void *getPointerTo(const size_t Pos) const noexcept = 0; + virtual const void *pointerTo(const size_t Pos) const noexcept = 0; }; // Nested namespace with an implementation for Message, consider it private. namespace { // Template class for an implementation of Message, definition below. template class LocalMessage; // Initializes Arena with values from const references of Types. // NOTE: Arena needs to be a valid pointer to a memory area big enough for // values of Types. template inline void createMessageElements(void *const Arena, const Types &... Ts) noexcept; // NOTE: This terminal case is used for both constant references and movable // references. template inline void createMessageElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, const AtomConstant &, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createMessageElement(Arena, Offsets, Ts...); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast(static_cast(Arena) + Offsets[Pos]))) Type(T); createMessageElement(Arena, Offsets, Ts...); } template inline void createMessageElements(void *const Arena, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); createMessageElement<0>(Arena, LocalMessage::Offsets, T, Ts...); } // Initializes Arena with values from movable references of Types. // NOTE: Arena needs to be a valid pointer to a memory area big enough for // values of Types. template inline void createMessageElements(void *const Arena, Types &&... Ts) noexcept; template inline void createMessageElement(void *const Arena, const std::vector &Offsets, AtomConstant &&, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createMessageElement(Arena, Offsets, std::move(Ts)...); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast( static_cast(Arena) + Offsets[Pos]))) Type(std::move(T)); createMessageElement(Arena, Offsets, std::move(Ts)...); } template inline void createMessageElements(void *const Arena, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); createMessageElement<0>(Arena, LocalMessage::Offsets, std::move(T), std::move(Ts)...); } // Destroyes values of Types stored in Arena. // NOTE: Arena needs to be a valid pointer to a memory area where values of // Types are stored. template inline void destroyMessageElements(void *const Arena) noexcept; template inline void destroyMessageElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyMessageElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyMessageElement(Arena, Offsets); } template inline void destroyMessageElements(void *const Arena) noexcept { destroyMessageElement<0, Type, Types...>( Arena, LocalMessage::Offsets); } // Implementation of the template LocalMessage. Provides facilities for storing // values as a Message. template class LocalMessage : public Message { public: // Type Token for the stored values. // NOTE: Only for compile-time checks, static member is not defined. static constexpr Token ST = TypeToken::type, typename std::decay::type...>::Value; // Offsets required to access values in Arena. static const std::vector Offsets; private: // A BLOB storing all the values one after the other. void *const Arena; // Generates offset for accessing values in Arena. - static std::vector getOffsets(void) noexcept { + static std::vector offsets(void) noexcept { Token T = ST; // Need a mutable copy. size_t I = 0; // Start indexing from position 0. std::vector O(lengthOfToken(T)); // Allocate vector of proper size. O[0] = 0; // First offset is always 0. while (!emptyToken(T)) { ASSERT(I < O.size()); // Calculate next offset based on the previous one. O[I + 1] = O[I] + sizeOfHeadOfToken(T); dropHeadOfToken(T), ++I; } ASSERT(I == O.size()); return O; } public: // Ctor for constant references. LocalMessage(const Type &T, const Types &... Ts) noexcept : Message(T, Ts...), Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(this->T == ST && Size == Offsets.size() && Arena != nullptr); // Sanity check. createMessageElements(Arena, T, Ts...); } // Ctor for movable referecens. LocalMessage(Type &&T, Types &&... Ts) noexcept : Message(T, Ts...), Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(this->T == ST && Size == Offsets.size() && Arena != nullptr); // Sanity check. createMessageElements(Arena, std::move(T), std::move(Ts)...); } // Dtor. ~LocalMessage(void) { destroyMessageElements(Arena); ::operator delete(Arena); } // Provides an untyped pointer for the constant value at Pos. - const void *getPointerTo(const size_t Pos) const noexcept override { + const void *pointerTo(const size_t Pos) const noexcept override { ASSERT(Pos < Offsets.size()); return static_cast(Arena) + Offsets[Pos]; } }; // Implementation of the static member field Offsets. template const std::vector LocalMessage::Offsets = - LocalMessage::getOffsets(); + LocalMessage::offsets(); } // End namespace template Message::Message(const Type &, const Ts &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Ts))); // Sanity check. LOG_TRACE("Creating Message with Token(" + to_string(T) + ")"); } template Message::message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } template Message::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 size_t Pos) const noexcept { ASSERT(Pos < Size); Token T_ = T; // NOLINT dropNOfToken(T_, Pos); return isHeadOfTokenTheSameType(T_); } template -const Type &Message::getValueAt(const size_t Pos) const noexcept { +const Type &Message::valueAt(const size_t Pos) const noexcept { ASSERT(Pos < Size && isTypeAt(Pos)); - return *static_cast(getPointerTo(Pos)); + return *static_cast(pointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP diff --git a/include/rosa/core/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp index dcda87e..4e821b5 100644 --- a/include/rosa/core/MessageMatcher.hpp +++ b/include/rosa/core/MessageMatcher.hpp @@ -1,168 +1,168 @@ /******************************************************************************* * * File: MessageMatcher.hpp * * Contents: Implementation of MessageMatcher. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGEMATCHER_HPP #define ROSA_CORE_MESSAGEMATCHER_HPP #include "rosa/core/Message.hpp" #include namespace rosa { // Template class with static functions type-checking a Message instance and // extracting stored values from Message instsances into std::tuple instances // with matching type arguments. template struct MessageMatcher; // Definition of MessageMatcher for non-empty lists of types, like Message // itself. template struct MessageMatcher> { // Type Token associated with the give TypeList. static constexpr Token T = TypeToken::Value; // Tells if stored values in Msg are matching types given as // TypeList, considering exact AtomConstants instead of AtomValues. static inline bool doesStronglyMatch(const Message &Msg) noexcept; // Gives a std::tuple with references to values stored in a type-matching Msg. // PRE: doesStronglyMatch(Msg) static inline std::tuple extractedValues(const Message &Msg) noexcept; }; // Convenience template alias turning a list of types into a TypeList for // MessageMatcher. template using MsgMatcher = MessageMatcher>; // Nested namespace with implementation for features of MessageMatcher, // consider it private. namespace { // Helper struct implementing type-checking and value extraction for // MessageMatcher. template struct MessageMatcherImpl; // Specialization handling the empty list of types. template <> struct MessageMatcherImpl { static inline bool doesStronglyMatchFrom(const Message &Msg, const size_t Pos) noexcept { // Matching EmptyTypeList only if reached the end of the stored types. return Pos == Msg.Size; } static inline std::tuple<> extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { // It is valid to extract an empty list only if we reached the end of // stored values. ASSERT(doesStronglyMatchFrom(Msg, Pos)); return std::tie(); } }; // Specialization handling an AtomValue in the head. template struct MessageMatcherImpl, Ts...>> { static inline bool doesHeadStronglyMatchAt(const Message &Msg, const size_t Pos) noexcept { // Matching an AtomConstant in the head if there is a type stored at Pos, // the stored type is AtomValue, and the corresponding value matches the // AtomValue V. return Pos < Msg.Size && Msg.isTypeAt(Pos) && - Msg.getValueAt(Pos) == V; + Msg.valueAt(Pos) == V; } static inline bool doesStronglyMatchFrom(const Message &Msg, const size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static inline std::tuple &, const Ts &...> extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { // Extracting for a non-empty list with a matching AtomConstant in the head // by getting the encoded AtomConstant and concatenating it with values // extracted for the rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(AtomConstant::Value), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; // Specialization handling an regular builtin type (not an AtomConstant) in the // head. template struct MessageMatcherImpl> { static inline bool doesHeadStronglyMatchAt(const Message &Msg, const size_t Pos) noexcept { // Matching the head if there is a type stored at Pos, and the stored type // is T. return Pos < Msg.Size && Msg.isTypeAt(Pos); } static inline bool doesStronglyMatchFrom(const Message &Msg, const size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static inline std::tuple extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { // Extracting for a non-empty list with a matching head by getting the // value for the head and concatenating it with values extracted for the // rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( - std::tie(Msg.getValueAt(Pos)), + std::tie(Msg.valueAt(Pos)), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; } // End namespace template bool MessageMatcher>::doesStronglyMatch( const Message &Msg) noexcept { // NOTE: Fail quick on T, then match against list with squashed integers the // way Tokens are generated. return T == Msg.T && MessageMatcherImpl>::Type>::doesStronglyMatchFrom(Msg, 0); } template std::tuple MessageMatcher>::extractedValues( const Message &Msg) noexcept { ASSERT(doesStronglyMatch(Msg)); // NOTE: Match against list with squashed integers as Tokens are generated. return MessageMatcherImpl>::Type>::extractedValuesFrom(Msg, 0); } } // End namespace rosa #endif // ROSA_CORE_MESSAGEMATCHER_HPP