diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp index e3acdfc..b11035a 100755 --- a/include/rosa/support/tokenized_storages.hpp +++ b/include/rosa/support/tokenized_storages.hpp @@ -1,629 +1,629 @@ //===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/tokenized_storages.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \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/log.h" #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 token_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 token_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 token_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; /// \defgroup TokenizedStorageForTypeList Implementation of /// rosa::TokenizedStorageForTypeList /// /// \brief Transforms a \c rosa::TypeList instance to the corresponding /// \c rosa::TokenizedStorage instance. /// /// A \c rosa::TypeList \c List instance can be turned into a corresponding \c /// rosa::TokenizedStorage instance as \code /// typename TokenizedStorageForTypeList::Type /// \endcode /// /// For example, the following expression evaluates to `true`: \code /// std::is_same>::Type, /// TokenizedStorage>::value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to transform template struct TokenizedStorageForTypeList; /// Implementation of the template for \c rosa::TypeList instances. template struct TokenizedStorageForTypeList> { using Type = 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 Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \p nullptr. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); 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 Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \c nullptr. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); createArenaElement<0>(Arena, TokenizedStorage::Offsets, std::move(Ts)...); } /// Destroys values allocated by \c createArenaElements. /// /// \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 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 Types types of values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre \p Arena is not \c nullptr. template inline void destroyArenaElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); 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 Types types of values to store template class TokenizedStorage : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = TypeToken...>::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 token_size_t N = lengthOfToken(T); // Number of types encoded in \c T. std::vector O(N); // Allocate vector of proper size. // Do nothing for 0 elements. if (N > 0) { token_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); + ASSERT((size_t)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((size_t)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 \p /// Types... are default constructible. TokenizedStorage(void) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Types()...); } /// Creates an instance from constant lvalue references. /// /// \param Ts values to store TokenizedStorage(const std::decay_t &... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Ts...); } /// Creates an instance from rvalue references. /// /// \param Ts values to store TokenizedStorage(std::decay_t &&... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. 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); ::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 token_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 token_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 token_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 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 T &valueAt(const token_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 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 T &valueAt(const token_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(); /// Specialization of the template \c rosa::TokenizedStorage for storing /// nothing. /// /// \note The specialization implements the interface defined by \c /// rosa::AbstractTokenizedStorage but most of the functions cannot be called /// because nothing is stored in instances of the class. template <> class TokenizedStorage<> : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = TypeToken<>::Value; /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. static const std::vector Offsets; /// Creates an instance. TokenizedStorage(void) noexcept = default; /// 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) override {} /// Tells how many values are stored in \p this object. /// /// \return `0` size_t size(void) const noexcept override { return 0; } /// Tells the type of the value stored at a position. /// /// \pre Do not call. TypeNumber typeAt(const token_size_t) const noexcept override { LOG_ERROR("Called unsupported function"); return TypeNumber(0); } /// Provides an untyped pointer for the value stored at a position. /// /// \pre Do not call. void *pointerTo(const token_size_t) noexcept override { LOG_ERROR("Called unsupported function"); return nullptr; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \pre Do not call. const void *pointerTo(const token_size_t) const noexcept override { LOG_ERROR("Called unsupported function"); return nullptr; } /// Tells if the value stored at a given index is of a given type. /// /// \pre Do not call. template bool isTypeAt(const size_t) const noexcept { LOG_ERROR("Called unsupported function"); return false; } /// Gives a reference of a value of a given type stored at a given index. /// /// \tparam T type to give a reference of /// \pre Do not call. template T &valueAt(const token_size_t) noexcept { LOG_ERROR("Called unsupported function"); return *static_cast(nullptr); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T type to give a reference of /// /// \pre Do not call. template const T &valueAt(const token_size_t) const noexcept { LOG_ERROR("Called unsupported function"); // \note Just use the non-const implementation as that does not modify // \p this object. return *static_cast(nullptr); } }; } // End namespace rosa #endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP