diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index d7aa5e2..ca3d5de 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,474 +1,477 @@ /***************************************************************************//** * * \file rosa/core/Message.hpp * * \author David Juhasz (david.juhasz@tuwien.ac.at) * * \date 2017 * * \brief Declaration of `rosa::Message` base-class. * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGE_HPP #define ROSA_CORE_MESSAGE_HPP #include "rosa/support/log.h" #include "rosa/support/type_token.hpp" #include "rosa/core/forward_declarations.h" #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. /// /// A `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 `rosa::Message` instance remains valid /// only as long as the owning `rosa::Message` object is not destroyed. 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 `rosa::Message`. /// /// \pre `Type` and `Types` are all built-in types and the number of stored /// values does not exceed `rosa::token::MaxTokenizableListSize`. template Message(const Type &, const Types &...) noexcept; /// No copying and moving of `rosa::Message` instances. ///@{ Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; ///@} public: /// Creates a `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 `rosa::Message` /// \param Ts optional further values to include in the `rosa::Message` /// /// \return new `message_t` object created from the given arguments template static message_t create(const Type &T, const Types &... Ts) noexcept; /// Creates a `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 `rosa::Message` /// \param Ts optional further values to include in the `rosa::Message` /// /// \return new `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 `this` object. /// /// A valid, non-empty `rosa::Token` representing the types of the values /// stored in `this` object. const Token T; /// The number of values stored in `this` object. /// /// That is the number of types encoded in `T`. const size_t Size; /// Destroys `this` object. virtual ~Message(void); /// Tells if the value stored at a given index is of a given type. /// /// \note Any `rosa::AtomConstant` is encoded in `rosa::Token` as /// the `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 `Type` /// /// \return if the value at index `Pos` of type `Type` /// /// \pre `Pos` is a valid index:\code /// Pos < Size /// \endcode template bool isTypeAt(const 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 `Type` for the value stored at index `Pos` /// /// \pre `Pos` is a valid index and the value at index `Pos` is of type /// `Type`:\code /// Pos < Size && isTypeAt(Pos) /// \endcode template const Type &valueAt(const 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 `Pos` /// /// \pre `Pos` is a valid index:\code /// Pos < Size /// \endcode virtual const void *pointerTo(const size_t Pos) const noexcept = 0; }; /// Nested namespace with implementation for `rosa::Message`, consider it /// private. namespace { /// Template class for an implementation of `rosa::Message`. /// /// \tparam Types types whose values are to be stored template class LocalMessage; /// Initializes a pre-allocated memory area with values from constant lvalue /// references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in `Arena` /// /// \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; /// \defgroup createMessageElement from const lvalue references /// /// Stores values from constant lvalue references into a pre-allocated memory /// area. /// /// \note To be used by the implementation of `rosa::createMessageElements`. /// /// \todo Document these functions. ///@{ /// \note This terminal case is used for both constant lvalue references and /// value 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 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 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...); } ///@} /// Implementation of the template. /// /// \tparam Type the type of the mandatory first value to store /// \tparam Types types of any further values to store /// /// \param Arena pre-allocated memory area to store values to /// \param T the first value to store in `Arena`˛ /// \param Ts optional further values to store in `Arena` /// /// \pre `Arena` is not `nullptr`. 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 a pre-allocated memory area with values from rvalue references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in `Arena` /// /// \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; /// \defgroup createMessageElement from rvalue references /// /// Stores values from rvalue references into a pre-allocated memory area. /// /// \note To be used by the implementation of `rosa::createMessageElements`. /// /// \todo Document these functions. ///@{ 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 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)...); } ///@} /// Implementation of the template. /// /// \tparam Type the type of the mandatory first value to store /// \tparam Types types of any further values to store /// /// \param Arena pre-allocated memory area to store values to /// \param T the first value to store in `Arena`˛ /// \param Ts optional further values to store in `Arena` /// /// \pre `Arena` is not `nullptr`. 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)...); } /// Destroys values allocated by `rosa::createMessageElements`. /// /// \tparam Type type of the mandatory first value stored in `Arena` /// \tparam Types futher types whose values are stored in `Arena` /// /// \param Arena the memory area to destroy values from /// /// \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; /// \defgroup destroyMessageElement /// /// Destroys values from a memory area. /// /// \note To be used by the implementation of `rosa::destroyMessageElements`. /// /// \todo Document these functions. ///@{ 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); } ///@} /// Implementation of the template. /// /// \tparam Type the type of the mandatory first value to destroy /// \tparam Types types of any further values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre `Arena` is not `nullptr`. template inline void destroyMessageElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); destroyMessageElement<0, Type, Types...>( Arena, LocalMessage::Offsets); } /// Implementation of the template `rosa::LocalMessage` providing facilities for /// storing values as a `rosa::Message` object. /// /// \tparam Type type of the first mandatory value of the `Message` /// \tparam Types of any further values template class LocalMessage : public Message { public: /// `rosa::Token` for the stored values. /// /// \note Only for compile-time checks! This static member is not defined /// because it must be the same as `rosa::Message::T`. static constexpr Token ST = TypeToken::type, typename std::decay::type...>::Value; /// Byte offsets to access stored values in `Arena`. static const std::vector Offsets; private: /// A BLOB storing all the values one after the other. void *const Arena; /// Generates byte offsets for accessing values stored in `Arena`. /// /// \return `std::vector` containing byte offsets for accessing values stored /// in `Arena` 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()); + Token T = ST; // Need a mutable copy. + const size_t N = lengthOfToken(T); // Number of types encoded in T. + size_t I = 0; // Start indexing from position 0. + std::vector O(N); // Allocate vector of proper size. + O[0] = 0; // First offset is always 0. + while (I < N - 1) { + ASSERT(I + 1 < O.size() && lengthOfToken(T) == N - I); // Calculate next offset based on the previous one. + // \note The offset of the last value is stored at O[N - 1], which is set + // when I == N - 2. Hence the limit of the loop. O[I + 1] = O[I] + sizeOfHeadOfToken(T); dropHeadOfToken(T), ++I; } - ASSERT(I == O.size()); + ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1); return O; } public: /// Creates an instance from constant lvalue references. /// /// \param T the mandatory first value to store in the `rosa::Message` object /// \param Ts optional further values to store in the `rosa::Message` object 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...); } /// Creates an instance from rvalue references. /// /// \param T the mandatory first value to store in the `rosa::Message` object /// \param Ts optional further values to store in the `rosa::Message` object 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)...); } // Destroys `this` object. ~LocalMessage(void) { destroyMessageElements(Arena); ::operator delete(Arena); } /// 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 `Pos` 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 `rosa::LocalMessage::Offsets`. template const std::vector LocalMessage::Offsets = LocalMessage::offsets(); } // 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) + ")"); } /// \note The implementation instantiates a private local template class /// `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 /// `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 size_t Pos) const noexcept { ASSERT(Pos < Size); Token T_ = T; // NOLINT dropNOfToken(T_, Pos); return isHeadOfTokenTheSameType(T_); } template const Type &Message::valueAt(const size_t Pos) const noexcept { ASSERT(Pos < Size && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP