diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp index 0f5bdbf..36d10fb 100755 --- a/include/rosa/support/tokenized_storages.hpp +++ b/include/rosa/support/tokenized_storages.hpp @@ -1,510 +1,500 @@ //===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/tokenized_storages.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Definition of storage helper template for storing values in a /// type-safe way based on type tokens. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #include "rosa/support/type_token.hpp" #include #include namespace rosa { /// Defines a simple interface for storing and accessing values of different /// types. /// /// While the interface provides features to access values and know their /// types, it is the users responsibility to use particular values according to /// their actual types. No facilities for type-safe access of values is /// provided by the class. /// /// \see \c rosa::TokenizedStorage for a type-safe specialization of the /// interface. class AbstractTokenizedStorage { protected: /// Protected constructor restricts instantiation for derived classes. AbstractTokenizedStorage(void) noexcept = default; public: /// No copying and moving of \c rosa::AbstractTokenizedStorage instances. ///@{ AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete; AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete; ///@} /// Destroys \p this object. virtual ~AbstractTokenizedStorage(void) noexcept = default; /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object virtual size_t size(void) const noexcept = 0; /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode virtual TypeNumber typeAt(const size_t Pos) const noexcept = 0; /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped 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 void *pointerTo(const size_t Pos) noexcept = 0; /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode virtual const void *pointerTo(const size_t Pos) const noexcept = 0; }; /// Template class storing values and providing dynamic type-safe access to /// them in a lightweight way based on type tokens. /// /// \see rosa/support/type_token.hpp /// /// \tparam Types types whose values are to be stored template class TokenizedStorage; /// Nested namespace with implementation for \c rosa::TokenizedStorage, consider /// it private. namespace { /// 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 \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept; /// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements /// /// Stores values from constant lvalue references into a pre-allocated memory /// area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ /// \note This terminal case is used for both constant lvalue references and /// value references. template inline void createArenaElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createArenaElement(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); createArenaElement(Arena, Offsets, Ts...); } template inline void createArenaElement(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; createArenaElement(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 +/// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to -/// \param T the first value to store in \p Arena˛ -/// \param Ts optional further values to store in \p Arena +/// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \p nullptr. -template -inline void createArenaElements(void *const Arena, const Type &T, +template +inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, T, - Ts...); + createArenaElement<0>(Arena, TokenizedStorage::Offsets, 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 \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept; /// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements /// /// Stores values from rvalue references into a pre-allocated memory area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ template inline void createArenaElement(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)); createArenaElement(Arena, Offsets, std::move(Ts)...); } template inline void createArenaElement(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; createArenaElement(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 +/// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to -/// \param T the first value to store in \p Arena -/// \param Ts optional further values to store in \p Arena +/// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \c nullptr. -template -inline void createArenaElements(void *const Arena, Type &&T, - Types &&... Ts) noexcept { +template +inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, - std::move(T), std::move(Ts)...); + createArenaElement<0>(Arena, TokenizedStorage::Offsets, + std::move(Ts)...); } /// Destroys values allocated by \c createArenaElements. /// -/// \tparam Type type of the mandatory first value stored in \p Arena -/// \tparam Types futher types whose values are stored in \p Arena +/// \tparam Types types whose values are stored in \p Arena /// /// \param Arena the memory area to destroy values from /// /// \note \p Arena needs to be a valid pointer to a memory area where values of /// \p Types are stored. -template +template inline void destroyArenaElements(void *const Arena) noexcept; /// \defgroup destroyArenaElement Implementation of destroying arena elements /// /// Destroys values from a memory area. /// /// \note To be used by the implementation of \c destroyArenaElements. /// /// \todo Document these functions. ///@{ template inline void destroyArenaElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyArenaElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyArenaElement(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 +/// \tparam Types types of values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre \p Arena is not \c nullptr. -template +template inline void destroyArenaElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); - destroyArenaElement<0, Type, Types...>( - Arena, TokenizedStorage::Offsets); + destroyArenaElement<0, Types...>(Arena, TokenizedStorage::Offsets); } } // End namespace /// Implementation of the template \c rosa::TokenizedStorage as a /// specialization of \c rosa::AbstractTokenizedStorage. /// /// The class provides facilities for storing values and providing type-safe /// access to them. /// -/// \tparam Type type of the first mandatory value to store -/// \tparam Types of any further values to store -template -class TokenizedStorage : public AbstractTokenizedStorage { +/// \tparam Types types of values to store +template +class TokenizedStorage : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = - TypeToken::type, - typename std::decay::type...>::Value; + TypeToken::type...>::Value; /// Byte offsets to access stored values in \c rosa::TokenizedStorage::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 /// \c rosa::TokenizedStorage::Arena. /// /// \return \c std::vector containing byte offsets for accessing values stored /// in \c rosa::TokenizedStorage::Arena static std::vector offsets(void) noexcept { Token T = ST; // Need a mutable copy. const size_t N = lengthOfToken(T); // Number of types encoded in \c T. - size_t I = 0; // Start indexing from position \c 0. std::vector O(N); // Allocate vector of proper size. - O[0] = 0; // First offset is always \c 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; + // Do nothing for 0 elements. + if (N > 0) { + size_t I = 0; // Start indexing from position \c 0. + O[0] = 0; // First offset is always \c 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 + 1 == O.size() && lengthOfToken(T) == 1); } - ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1); return O; } public: /// Creates an instance with default values. /// - /// \note This constructor requires that all actual template arguments \c Type - /// and \c Types... are default constructible. + /// \note This constructor requires that all actual template arguments \p + /// Types... are default constructible. TokenizedStorage(void) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. - createArenaElements(Arena, Type(), Types()...); + createArenaElements(Arena, Types()...); } /// Creates an instance from constant lvalue references. /// - /// \param T the mandatory first value to store - /// \param Ts optional further values to store - TokenizedStorage(const Type &T, const Types &... Ts) noexcept + /// \param Ts values to store + TokenizedStorage(const Types &... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. - createArenaElements(Arena, T, Ts...); + createArenaElements(Arena, Ts...); } /// Creates an instance from rvalue references. /// - /// \param T the mandatory first value to store - /// \param Ts optional further values to store - TokenizedStorage(Type &&T, Types &&... Ts) noexcept + /// \param Ts values to store + TokenizedStorage(Types &&... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. - createArenaElements(Arena, std::move(T), std::move(Ts)...); + createArenaElements(Arena, std::move(Ts)...); } /// No copying and moving of \c rosa::TokenizedStorage instances. /// /// \note This restriction may be relaxed as moving should be easy to /// implement, only requires the possiblity to validate Arena pointer. ///@{ TokenizedStorage(const TokenizedStorage&) = delete; TokenizedStorage &operator=(const TokenizedStorage&) = delete; TokenizedStorage(TokenizedStorage&& Other) = delete; TokenizedStorage &operator=(TokenizedStorage&&) = delete; ///@} // Destroys \p this object. ~TokenizedStorage(void) { - destroyArenaElements(Arena); + destroyArenaElements(Arena); ::operator delete(Arena); } /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object size_t size(void) const noexcept override { return Offsets.size(); } /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode TypeNumber typeAt(const size_t Pos) const noexcept override { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return headOfToken(TT); } /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode void *pointerTo(const size_t Pos) noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode const void *pointerTo(const size_t Pos) const noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// 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 T 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 T /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode template bool isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } /// Gives a reference of a value of a given type stored at a given index. /// /// \note The constant variant of the function relies on this implementation, /// the function may not modify \p this object! /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// - /// \return reference of \p Type for the value stored at index \p Pos + /// \return reference of \p T 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 T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode - template Type &valueAt(const size_t Pos) noexcept { - ASSERT(Pos < size() && isTypeAt(Pos)); - return *static_cast(pointerTo(Pos)); + template T &valueAt(const size_t Pos) noexcept { + ASSERT(Pos < size() && isTypeAt(Pos)); + return *static_cast(pointerTo(Pos)); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T 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 + /// \return constant reference of \p T 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 T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode - template const Type &valueAt(const size_t Pos) const noexcept { + template const T &valueAt(const size_t Pos) const noexcept { // \note Just use the non-const implementation as that does not modify // \p this object. return const_cast(this)->valueAt(Pos); } }; // Implementation of the static member field \c rosa::TokenizedStorage::Offsets. -template -const std::vector TokenizedStorage::Offsets = - TokenizedStorage::offsets(); +template +const std::vector + TokenizedStorage::Offsets = TokenizedStorage::offsets(); } // End namespace rosa #endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP