diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index b4033cb..4d280b5 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,169 +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; -// A simple implementation of the Message interface with two hardcoded types. -class MyMessage : public Message { - const uint8_t U8; // First value - const uint16_t U16; // Second value -public: - MyMessage(const uint8_t U8, const uint16_t U16) - : Message(U8, U16), U8(U8), U16(U16) {} - - const void *getPointerTo(const size_t Pos) const noexcept override { - switch (Pos) { - default: - ROSA_CRITICAL("wrong position"); - case 0: - return &U8; - case 1: - return &U16; - } - } -}; - -// Another implementation of the Message interface with an AtomValue. -class MyAtomMessage : public Message { - const AtomValue A; // First value -public: - template - MyAtomMessage(AtomConstant C) : Message(C), A(V) {} - - const void *getPointerTo(const size_t Pos) const noexcept override { - switch (Pos) { - default: - ROSA_CRITICAL("wrong position"); - case 0: - return &A; - } - } -}; - int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << std::endl; auto &Log = LOG_INFO_STREAM << std::endl; // Message interface. - MyMessage Msg(1, 2); - Log << "Checking on 'MyMessage(1, 2)' that is of 'Message with " - "TypeList<>':" + 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)) << std::endl << " Value at pos 1: " << Msg.getValueAt(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; - MyAtomMessage AMsg(A); - Log << "Checking on 'MyAtomMessage(AtomConstant::Value)' " - "that is of 'Message with TypeList>':" + 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; + Log << "** Handling 'Message with TypeList>'." + << std::endl; })); auto &I = *IP; // Get a reference from the pointer. - Log << "Invoking a function of signature 'void(MyAtom) noexcept':" + 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 + 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([&](uint8_t, uint16_t) { - LOG_TRACE("** Handling 'Message with TypeList'"); + Invoker::F([&Log](uint8_t, uint16_t) { + Log << "** Handling 'Message with TypeList'" + << std::endl; }), - Invoker::F([&](MyAtom) { - LOG_TRACE("** Handling 'Message with TypeList'"); + Invoker::F([&Log](MyAtom) { + Log << "** Handling 'Message with " + "TypeList>'" << std::endl; })}; - MyAtomMessage ANMsg(MyNAtom::Value); + auto PANMsg = Message::create(MyNAtom::Value); + auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " - "{ Invoker::F, Invoker::F }':" + "{ 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 + Log << " 'Message with TypeList>'" << std::endl << " can handle: " << Handler.canHandle(AMsg) << std::endl << " handling..." << std::endl; Handler(AMsg); - Log << " 'Message with TypeList'" << std::endl + 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 74d124a..d02586c 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,92 +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(Type, Ts...) noexcept; + 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; 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; }; +// 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 { + 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 { + 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(); + +} // End namespace + template -Message::Message(Type, Ts...) noexcept : T(TypeToken::Value), - Size(lengthOfToken(T)) { +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 { ASSERT(Pos < Size && isTypeAt(Pos)); return *static_cast(getPointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP