diff --git a/include/rosa/support/iterator/split_tuple_iterator.hpp b/include/rosa/support/iterator/split_tuple_iterator.hpp index f64b346..f5bb831 100644 --- a/include/rosa/support/iterator/split_tuple_iterator.hpp +++ b/include/rosa/support/iterator/split_tuple_iterator.hpp @@ -1,427 +1,431 @@ //===-- rosa/support/iterator/split_tuple_iterator.hpp ----------*- C++ -*-===/ // // The RoSA Framework // //===----------------------------------------------------------------------===/ /// /// \file rosa/support/iterator/split_tuple_iterator.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2019 /// /// \brief Facilities to split iterators of \c std::tuple into iterators of /// their elements. /// //===----------------------------------------------------------------------===/ #ifndef ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP #define ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP #include "rosa/support/debug.hpp" #include "rosa/support/sequence.hpp" #include #include #include #include namespace rosa { namespace iterator { /// Anonymous namespace providing implementation for splitting tuple iterators; /// consider it private. namespace { /// Iterator of values provided by a buffer based on an index /// /// \tparam TB buffer providing values to iterate /// \tparam I index of the values to iterator from \p TB /// /// \note \p TB is expected to implemnet an interface matching that of \c /// TupleIteratorBuffer. template class SplittedElementIterator { public: /// Type alias for the values the iterator iterates. using T = typename TB::template element_type; /// \defgroup SplittedElementIteratorTypedefs Typedefs of /// \c SplittedElementIterator /// /// Standard `typedef`s for iterators. /// ///@{ typedef std::input_iterator_tag iterator_category; ///< Category of the iterator. typedef T value_type; ///< Type of values iterated over. typedef std::size_t difference_type; ///< Type to identify distance. typedef T *pointer; ///< Pointer to the type iterated over. typedef T &reference; ///< Reference to the type iterated ver. ///@} /// Creates a new instance. /// /// \param Buffer buffer providing values for \p this object /// /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared /// among iterators for its element positions. SplittedElementIterator(std::shared_ptr Buffer) : Buffer(Buffer), Value() { // \c Value is initialized empty so the first incrementation here will read // the first value. ++(*this); } /// Creates an empty new instance. SplittedElementIterator(void) noexcept : Buffer(), Value() {} /// Pre-increment operator. /// /// The implementation reads the next value from \c Buffer. If no more values /// can be provided by \c Buffer, \p this object becomes empty and the /// operator has no further effect. /// /// \return \p this object after incrementing it. SplittedElementIterator &operator++() { if (Buffer->template hasMore()) { Value = Buffer->template next(); } else { // Reached end of range, remove reference of \c Buffer. Buffer.reset(); } return *this; } /// Post-increment operator. /// /// The implementation uses the pre-increment operator and returns a copy of /// the original state of \p this object. /// /// \return \p this object before incrementing it. SplittedElementIterator operator++(int) { SplittedElementIterator Tmp(*this); ++(*this); return Tmp; } /// Returns a constant reference to the current value. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant reference to the current value. const T &operator*(void)const noexcept { return Value; } /// Returns a constant pointer to the current value. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant pointer to the current value. const T *operator->(void)const noexcept { return &Value; } /// Tells if \p this object is equal to another one. /// /// Two \c SplittedElementIterator instances are equal if and only if they are /// the same or both are empty. /// /// \param RHS other object to compare to /// /// \return whether \p this object is equal with \p RHS bool operator==(const SplittedElementIterator &RHS) const noexcept { return ((this == &RHS) || (!Buffer && !RHS.Buffer)); } /// Tells if \p this object is not equal to another one. /// /// \see SplittedElementIterator::operator== /// /// \param RHS other object to compare to /// /// \return whether \p this object is not equal with \p RHS. bool operator!=(const SplittedElementIterator &RHS) const noexcept { return !((*this) == RHS); } private: std::shared_ptr Buffer; ///< Buffer providing values for \p this object T Value; ///< The current value of the iterator }; ///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of ///types into a \c std::tuple of container of those types. /// /// The new type is used for buffering elements of tuples separately. /// ///@{ /// Template declaration. /// /// \tparam T type to convert /// /// \note The template is defined only when \p T is a \c std::tuple. /// /// Usage for a type \c Tuple as:\code /// typename TupleBufferContainer::Type /// \endcode template struct TupleBufferContainer; /// Template definition for \c std::tuple. template struct TupleBufferContainer> { /// The converted type. using Type = std::tuple...>; }; ///@} /// Convenience template type alias for easy use of \c TupleBufferContainer. /// /// Converts a \c std::tuple of types into a \c std::tuple of container of those /// types. /// /// \tparam Tuple type to convert template using tuple_buffer_container_t = typename TupleBufferContainer::Type; /// Buffer for element values for iterator of \c std::tuple. /// /// The class can be instantiated with a range of iterator of \c std::tuple and /// provides an interface for accessing elements of the iterated tuples one by /// one. /// /// \note The class is utilized by \c SplittedElementIterator. /// /// \tparam TupleIterator the iterator type that provides values of \c /// std::tuple /// /// \todo Consider thread safety of the class. template class TupleIteratorBuffer { public: /// Type alias for the value type of \p TupleIterator. /// \note The type is expected to be \c std::tuple. using iterator_value_type = typename TupleIterator::value_type; /// The number of elements of \c iterator_value_type. static constexpr size_t iterator_value_size = std::tuple_size_v; /// Template type alias to get element types of \c iterator_value_type by /// index. /// \tparam I the index of the element template using element_type = typename std::tuple_element::type; + /// Type alias for index sequence for accessing elements of \c + /// iterator_value_type. + using element_idx_seq_t = seq_t; + /// Creates a new instance. /// /// \param Begin the beginning of the iterator range to buffer /// \param End the end of the iterator range to buffer TupleIteratorBuffer(TupleIterator &&Begin, const TupleIterator &End) noexcept : Iterator(Begin), End(End), Buffer() {} /// Tells whether there is any more value in the handled range for index \p I. /// /// \tparam I the index of the element to check /// /// \return whether there is more value for index \p I template bool hasMore(void) const noexcept { const auto &ElementBuffer = std::get(Buffer); // There is more value if some has already been buffered or the end of the // handled range is not reached yet. return !ElementBuffer.empty() || Iterator != End; } /// Gives the next value from the handled range for index \p I. /// /// \note The function should not be called if there is no more value for /// index \p I (i.e., \c hasMore() returns \c false). If it is called in /// that situation, it returns the default value of \c element_type. /// /// \tparam I the index of the element to fetch next value for /// /// \return the next value for index \p I template element_type next(void) noexcept { auto &ElementBuffer = std::get(Buffer); if (ElementBuffer.empty()) { // This position needs a new value from Iterator if there is more. if (Iterator != End) { // Push next value for all elements. - push(*Iterator++, seq_t()); + push(*Iterator++, element_idx_seq_t()); } else { // No more values; called when hasMore() is false. // Return default value. return element_type(); } } // Buffer is not empty here, return the next value. ASSERT(!ElementBuffer.empty() && "Unexpected condition"); // Get the next value and also pop it from the buffer. const element_type Elem = ElementBuffer.front(); ElementBuffer.pop(); return Elem; } private: /// Buffers next tuple value elementwise into element containers. /// /// \tparam S0 indices for accessing elements of \c std::tuple /// /// \param Tuple the values to buffer /// /// \note The second parameter provides indices as template parameter \p S0 /// and its actual value is ignored. template void push(const iterator_value_type &Tuple, Seq) noexcept { (std::get(Buffer).push(std::get(Tuple)), ...); } TupleIterator Iterator; ///< Current input iterator const TupleIterator End; ///< End of iterator range to handle tuple_buffer_container_t Buffer; ///< Container for elementwise buffering }; /// Template type alias for iterator of an element based on buffered iterator of /// \c std::tuple. /// /// The alias utilizes \c SplittedElementIterator for iterating over the values /// provided by \p TB. /// /// \tparam TB buffer providing values to iterate /// \tparam I index of the values to iterator from \p TB template using element_iterator_t = SplittedElementIterator; /// Template type alias for iterator-range of an element based on buffered /// iterator of \c std::tuple. /// /// \tparam TB buffer providing values to iterate /// \tparam I index of the values to iterator from \p TB template using element_iterator_range_t = std::pair, element_iterator_t>; ///\defgroup ElementIteratorRanges Type converter turning a buffer of \c /// std::tuple into a \c std::tuple of corresponding \c /// element_iterator_range_t. /// ///@{ /// Template declaration. /// /// \tparam TB buffer providing values /// \tparam S type providing indices for accessing elements of \c std::tuple /// /// \note \p TB is expected to implement an interface matching that of \c /// TupleIteratorBuffer. /// /// \note The template is defined only when \p S is a \c rosa::Seq. /// /// Usage for a proper buffer type \c TB:\code -/// typename ElementIteratorRanges>::Type +/// typename ElementIteratorRanges::Type /// \endcode template struct ElementIteratorRanges; /// Template definition. template struct ElementIteratorRanges> { /// The converted type. using Type = std::tuple...>; }; ///@} /// Convenience template type alias for easy use of \c ElementIteratorRanges. /// /// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c /// element_iterator_range_t. /// /// \tparam TB buffer providing values /// /// \note \p TB is expected to implement an interface matching that of \c /// TupleIteratorBuffer. template using element_iterator_ranges_t = - typename ElementIteratorRanges>::Type; + typename ElementIteratorRanges::Type; /// Template type alias for turning an iterator of \c std::tuple into /// corresponding \c element_iterator_ranges_t. /// /// The alias utilizes \c TupleIteratorBuffer for buffering the values provided /// by \p TupleIterator. The elementwise iterators are \c /// SplittedElementIterator. /// /// \tparam TupleIterator iterator of \c std::tuple template using splitted_tuple_iterator_ranges_t = element_iterator_ranges_t>; /// Creates elementwise iterator ranges for an iterator range of \c std::tuple. /// /// \note Implementation for \c rosa::iterator::splitTupleIterator. /// /// \tparam TupleIterator iterator of \c std::tuple /// \tparam S0 indices for accessing elements of \c std::tuple /// /// \param Begin the beginning of the iterator range to handle /// \param End the end of the iterator range to handle /// /// \note The last parameter provides indices as template parameter \p S0 and /// its actual value is ignored. /// /// \return \c std::tuple of elementwise iterator ranges corresponding to \p /// Begin and \p End template splitted_tuple_iterator_ranges_t splitTupleIteratorImpl(TupleIterator &&Begin, const TupleIterator &End, Seq) noexcept { using TB = TupleIteratorBuffer; // Create a buffer in shared pointer. The buffer will be destructed once // all corresponding element iterators (created below) have reached the end of // the handled range or have been destructed. auto Buffer = std::make_shared(std::move(Begin), End); return {std::make_pair(element_iterator_t(Buffer), element_iterator_t())...}; } } // End namespace /// Creates elementwise iterator ranges for an iterator range of \c std::tuple. /// /// \note The implementation utilizes \c splitTupleIteratorImpl. /// /// Obtain element iterator ranges for an iterator range \c Begin and \c End of /// iterator type \c TupleIterator of \c std::tuple as \code /// auto [T1Range, T2Range, T3Range] = splitTupleIterator(std::move(Begin), End); /// auto [T1Begin, T1End] = T1Range; /// auto [T2Begin, T2End] = T2Range; /// auto [T3Begin, T3End] = T3Range; /// \endcode /// /// The function returns a tuple of ranges (i.e., \c std::pair of iterators), /// which can be assigned to variables using structured binding. The ranges can /// be dissected into begin and end iterators by separate structured bindings. /// /// The function moves its first argument (i.e., \p Begin cannot be used after /// splitting it). If the first argument is a variable, it needs to be moved /// explicitly by \c std::move() as in the example. /// /// \tparam TupleIterator iterator of \c std::tuple /// /// \param Begin the beginning of the iterator range to handle /// \param End the end of the iterator range to handle /// /// \return \c std::tuple of elementwise iterator ranges corresponding to \p /// Begin and \p End template splitted_tuple_iterator_ranges_t splitTupleIterator(TupleIterator &&Begin, const TupleIterator &End) noexcept { return splitTupleIteratorImpl( std::move(Begin), End, seq_t>()); } } // End namespace iterator } // End namespace rosa #endif // ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP