/*! * \file utl/dev/dev_iterators.h * \brief Iterator collection for devices. * * Copyright (C) 2018 Christos Choutouridis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef __utl_dev_dev_iterators_h__ #define __utl_dev_dev_iterators_h__ #include #include namespace utl { /*! * \ingroup Devices * \brief Iterator collection. */ //!@{ /*! * Common template Iterator trait dispatcher for the STL requirement * type definitions. * Our iterators inherit from specializations of this type */ template struct dev_iterator_traits { }; //! Partial specialization for pointer types. template struct dev_iterator_traits<_Cat, _Tp*, _Diff> { typedef _Cat iterator_category; typedef _Tp value_type; typedef _Diff difference_type; typedef _Tp* pointer; typedef _Tp& reference; }; //! Partial specialization for const pointer types. template struct dev_iterator_traits<_Cat, const _Tp*, _Diff> { typedef _Cat iterator_category; typedef _Tp value_type; typedef _Diff difference_type; typedef const _Tp* pointer; typedef const _Tp& reference; }; /* * ================ Output device Iterator ================= */ /*! * \brief * Output device iterator type. We "future call" interface methods * from owner class to provide iterator functionality. * \param container_t Container/parent type * \param iter_t Iterator data type (pointer to container_t::value_type) * \param streamsize Stream size */ template class outdev_it { //! iterator type local name using iterator_t = outdev_it ; //! STL iterator traits "forwarding" //!@{ public: using iterator_category = typename dev_iterator_traits ::iterator_category; using value_type = typename dev_iterator_traits ::value_type; using difference_type = typename dev_iterator_traits ::difference_type; using pointer = typename dev_iterator_traits ::pointer; using reference = typename dev_iterator_traits ::reference; //!@} using type = iterator_t; //!< Export type as identity meta-function //! #define-like enumerator for Cursor enum Cursor { beg = 0, //!< Points the first item eos = streamsize, //!< Points one place after last item }; private: container_t* owner_; /*!< * Pointer to parent/owner device class. Constructor demands * owner container in order to access data. Considering the data * don't "live" in memory. */ size_t cursor_ {beg}; //!< virtual cursor for comparison operators /*! * \name Constructor / Destructor * \note * We can not provide a default constructor as long as we depend * on container_t (the owner type). */ //!@{ public: //! \brief Basic constructor explicit outdev_it (container_t* owner, size_t cursor =beg) noexcept : owner_{owner}, cursor_{cursor} { } //! \brief Basic copy constructor explicit outdev_it (const iterator_t& it) noexcept : owner_ {const_cast(it.owner())}, cursor_ {it.cursor()} { } //! \brief Iterator to const-iterator conversion (as STL requires) //! \param it Iterator reference template outdev_it (const outdev_it< use_if_same_t <_It, typename container_t::pointer_type, container_t>, _It, streamsize >& it) noexcept : owner_ {const_cast(it.owner())}, cursor_ {it.cursor()} { } //! \brief Basic copy assignment operator iterator_t& operator= (const iterator_t& it) noexcept { owner_ = const_cast(it.owner()); cursor_ = it.cursor(); } //!@} //!@{ \name Public interface public: iterator_t& operator* () noexcept { return *this; } /*! * \brief * Assignment operation. Where the output method is invoked * \param value An instance of container_t basic type * \return This %iterator, for chained operations. */ iterator_t& operator= (const value_type& value) { owner_->put (value); return *this; } //!@{ ++ operators //! STL compatibility. No cursor template use_if_t <(S == 0), iterator_t&> operator++ () noexcept { *this; } template use_if_t <(S == 0), iterator_t&> operator++ (int) noexcept { *this; } //!@} /*! * The following are not requirements for output iterator. * We provide them nevertheless. * This kind of %iterator doesn't usually have a @a cursor in the * container. Assigning a value to the %iterator will * always forward the value to the output device. If though we pass * a streamsize value !=0 then the iterator will work using the cursor. * The user has to be careful to keep sync between ++'s and ='s operator * calls in the code. */ //!@{ template use_if_t <(S != 0), iterator_t&> operator++ () noexcept { ++cursor_; return *this; } template use_if_t <(S != 0), iterator_t> operator++ (int) noexcept { iterator_t ret = *this; ++cursor_; return ret; } //!@} //! Export container for comparison const container_t* owner () const noexcept { return owner_; } //! Export cursor for comparison const size_t cursor () const noexcept { return cursor_; } //!@} }; /*! * \brief * Equality comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid * \param lhs Left hand site iterator * \param rhs Right hand site iterator * \return True in equality */ template inline bool operator== (const outdev_it<_Cont, _It_L, _Size>& lhs, const outdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return ((lhs.cursor() == rhs.cursor()) && (lhs.owner() == rhs.owner())); } /*! * \brief * Inequality comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid * \param lhs Left hand site iterator * \param rhs Right hand site iterator * \return True in inequality */ template inline bool operator!= (const outdev_it<_Cont, _It_L, _Size>& lhs, const outdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return !(lhs == rhs); } /*! * \note * The following are not requirements for output iterator. * We provide them nevertheless. * \warn * Required: The rhs and lhs MUST belong to the same owner or * the result is undefined. */ //!@{ template inline bool operator< (const outdev_it<_Cont, _It_L, _Size>& lhs, const outdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() < rhs.cursor()); } template inline bool operator<= (const outdev_it<_Cont, _It_L, _Size>& lhs, const outdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() <= rhs.cursor()); } template inline bool operator> (const outdev_it<_Cont, _It_L, _Size>& lhs, const outdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() > rhs.cursor()); } template inline bool operator>= (const outdev_it<_Cont, _It_L, _Size>& lhs, const outdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() >= rhs.cursor()); } //!@} /*! * Output device iterator concept */ //! @{ #if defined _utl_have_concepts template concept bool Outdev_it = requires (T t) { // STL compatibility typename T::value_type; typename T::difference_type; typename T::pointer; typename T::reference; requires same_< typename T::iterator_category, std::output_iterator_tag >::value; {*t} -> auto&&; // is dereferencable, and return non-void {++t} -> T&; // is incrementable {t++} -> T; // Extras {t.owner()} ->auto&&; {t.cursor()} -> size_t; }; #else namespace outdev_it_details { using std::declval; //! Primary template to catch any non SPI interface types template struct is_outdev_it_ : false_ {}; //! template to catch a proper SPI interface type template struct is_outdev_it_ < _Tp, void_t < typename T::value_type, typename T::difference_type, typename T::pointer, typename T::reference, use_if_same_t < typename T::iterator_category, std::output_iterator_tag > > > : true_ {}; } /*! * Value meta-programming function for SPI interface checking * \param _Tp Type to check * \return True if _Tp is a spi interface */ template constexpr bool Outdev_it = outdev_it_details::is_outdev_it_<_Tp>::value; #endif //! @} /* * ================ Input device Iterator ================= */ /*! * \brief * Input device iterator type. We "future call" interface methods * from owner class to provide iterator functionality. * \param container_t Container/parent type * \param iter_t Iterator data type (pointer to container_t::value_type) * \param streamsize Stream size */ template class indev_it { using iterator_t = indev_it ; //!< iterator type local name //! STL iterator traits "forwarding" //!@{ public: using iterator_category = typename dev_iterator_traits ::iterator_category; using value_type = typename dev_iterator_traits ::value_type; using difference_type = typename dev_iterator_traits ::difference_type; using pointer = typename dev_iterator_traits ::pointer; using reference = typename dev_iterator_traits ::reference; //!@} using type = iterator_t; //!< Export type as identity meta-function //! #define-like enumerator for Cursor enum Cursor { beg = 0, //!< Points the first item eos = streamsize, //!< Points one place after last item init= -1 //!< Used as flag so we have to fetch the first item }; private: container_t* owner_; /*!< * Pointer to parent/owner device class. Constructor demands * owner container in order to access data. Considering the data * don't "live" in memory. */ size_t cursor_ {init}; //!< virtual cursor for comparison operators value_type value_ {}; //!< The current value, used as a buffer /*! * \name Constructor / Destructor * \note * We can not provide a default constructor as long as we depend * on container_t (the owner type). */ //!@{ public: //! \brief Basic constructor explicit indev_it (container_t* own, size_t cur =init) noexcept : owner_{own}, cursor_{cur}, value_{} { } //! \brief Basic copy constructor explicit indev_it (const iterator_t& it) noexcept : owner_ {const_cast(it.owner())}, cursor_{it.cursor()}, value_ {it.value()} { } //! \brief Iterator to const-iterator conversion (as STL requires) //! \param it Iterator reference template indev_it (const indev_it< use_if_same_t <_It, typename container_t::pointer_type, container_t>, _It, streamsize >& it) noexcept : owner_ {const_cast(it.owner())}, cursor_{it.cursor()}, value_ {it.value()} { } //! \brief Basic copy assignment operator iterator_t& operator= (const iterator_t& it) noexcept { owner_ = const_cast(it.owner()); cursor_ = it.cursor(); value_ = it.value(); } //!@} //!@{ \name Public interface public: /*! * De-reference operator. We just return the current value * \note * We have to make sure we retrieve the first item before the * first dereference. * \note * No end() place dereference check is made. */ reference operator* () noexcept { if (cursor_ == init) ++*this; return value_; } //! Arrow operator pointer operator-> () noexcept { if (cursor_ == init) ++*this; return &value_; } //! Pre increment. This is where the input method is invoked iterator_t& operator++ () noexcept { owner_->get (value_); ++cursor_; return *this; } //! Post increment. This is where the input method is invoked iterator_t operator++ (int) noexcept { iterator_t ret = *this; owner_->get (value_); ++cursor_; return *this; } //! Export container for comparison const container_t* owner () const noexcept { return owner_; } //! Export cursor for comparison const size_t cursor () const noexcept { return cursor_; } //! Export value for comparison const value_type& value () const noexcept { return value_; } //!@} }; /*! * \brief * Equality comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid * \param lhs Left hand site iterator * \param rhs Right hand site iterator * \return True in equality */ template inline bool operator== (const indev_it<_Cont, _It_L, _Size>& lhs, const indev_it<_Cont, _It_R, _Size>& rhs) noexcept { return ((lhs.cursor() == rhs.cursor()) && (lhs.owner() == rhs.owner()) && (lhs.value() == rhs.value())); } /*! * \brief * Inequality comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid * \param lhs Left hand site iterator * \param rhs Right hand site iterator * \return True in inequality */ template inline bool operator!= (const indev_it<_Cont, _It_L, _Size>& lhs, const indev_it<_Cont, _It_R, _Size>& rhs) noexcept { return ((lhs.cursor() != rhs.cursor()) || (lhs.owner() != rhs.owner()) || (lhs.value() != rhs.value())); } /*! * \note * The following are not requirements for input iterator. * We provide them nevertheless. * \warn * Required: The rhs and lhs MUST belong to the same owner or * the result is undefined. */ //!@{ template inline bool operator< (const indev_it<_Cont, _It_L, _Size>& lhs, const indev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() < rhs.cursor()); } template inline bool operator<= (const indev_it<_Cont, _It_L, _Size>& lhs, const indev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() <= rhs.cursor()); } template inline bool operator> (const indev_it<_Cont, _It_L, _Size>& lhs, const indev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() > rhs.cursor()); } template inline bool operator>= (const indev_it<_Cont, _It_L, _Size>& lhs, const indev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() >= rhs.cursor()); } //!@} /*! * Input device iterator concept */ //! @{ #if defined _utl_have_concepts template concept bool Indev_it = requires (T t, const T ct) { // STL compatibility typename T::value_type; typename T::difference_type; typename T::pointer; typename T::reference; requires same_ < typename T::iterator_category, std::input_iterator_tag >::value; {*t} -> typename T::value_type; // is dereferencable {++t} -> T&; // is incrementable {t++} -> T; // Extras {ct.owner()} ->auto&&; {ct.cursor()} -> const size_t; {ct.value()} -> auto&&; }; #else namespace indev_it_details { using std::declval; //! Primary template to catch any non SPI interface types template struct is_indev_it_ : false_ {}; //! template to catch a proper SPI interface type template struct is_indev_it_ < _Tp, void_t < typename T::value_type, typename T::difference_type, typename T::pointer, typename T::reference, use_if_same_t < typename T::iterator_category, std::input_iterator_tag > > > : true_ {}; } /*! * Value meta-programming function for SPI interface checking * \param _Tp Type to check * \return True if _Tp is a spi interface */ template constexpr bool Indev_it = indev_it_details::is_indev_it_<_Tp>::value; #endif //! @} /* * ================= Indexed device Iterator (input) ================= */ /*! * \brief * Indexed device iterator type. We "future call" interface methods * from owner class to provide iterator functionality. * \note * This is a Input type iterator * \param container_t Container/parent type * \param iter_t Iterator data type (pointer to container_t::value_type) * \param N Max indexed/addressed items of device * Usual the last address is N-1 */ template class idxdev_it { using iterator_t = idxdev_it ; //!< iterator type local name //!@{ STL iterator traits "forwarding" public: using iterator_category = typename dev_iterator_traits ::iterator_category; using value_type = typename dev_iterator_traits ::value_type; using difference_type = typename dev_iterator_traits ::difference_type; using pointer = typename dev_iterator_traits ::pointer; using reference = typename dev_iterator_traits ::reference; //!@} using type = iterator_t; //!< Export type as identity meta-function //! #define-like enumerator for cursor enum Cursor { beg = 0, //!< Points the first item eos = N, //!< Points one place after last item }; private: container_t* owner_; /*!< * Pointer to parent/owner device class. Constructor demands * owner container in order to access data. Considering the data * don't "live" in memory. */ size_t cursor_{beg}; //!< virtual cursor for comparison operators /*! * Current value wrapper type, to used as put event catcher * owner_->get : Use v_ directly * owner_->put : Use operator= */ struct value_type_t { value_type v_; //!< Current value buffer to access via get value_type_t(value_type v =0) : v_{v} { } operator value_type() { return v_; } operator reference() { return v_; } value_type& operator= (const value_type& v) { v_ = v; //!< Catch any attempt to write to value_ owner_->put (v_, cursor_); //!< Sync the data back to device } } value_ {}; /*! * \name Constructor / Destructor * \note * We can not provide a default constructor as long as we depend * on container_t (the owner type). */ //!@{ public: //! \brief Basic constructor explicit idxdev_it (container_t* owner, size_t curor =beg) noexcept : owner_{owner}, cursor_{curor}, value_{} { } //! \brief Basic copy constructor explicit idxdev_it (const iterator_t& it) noexcept : owner_ {const_cast (it.owner())}, cursor_{it.cursor()}, value_ {it.value()} { } //! \brief Iterator to const iterator conversion (as STL requires) //! \param it Iterator reference template idxdev_it (const idxdev_it< use_if_same_t <_It, typename container_t::pointer_type, container_t>, _It, N >& it) noexcept : owner_ {const_cast (it.owner())}, cursor_{it.cursor()}, value_ {it.value()} { } //! \brief Basic copy assignment operator iterator_t& operator= (const iterator_t& it) noexcept { owner_ = const_cast(it.owner()); cursor_ = it.cursor(); value_ = it.value(); } //!@} //!@{ \name Public interface public: /*! * De-reference operator. This is where the input method is invoked * \note * No end() place dereference check is made. */ reference operator* () noexcept { owner_->get (value_.v_, cursor_); return value_; } //! Arrow operator. This is where the input method is invoked pointer operator-> () noexcept { owner_->get (value_.v_, cursor_); return &value_; } //! Pre increment. iterator_t& operator++ () noexcept { ++cursor_; return *this; } //! Post increment. iterator_t operator++ (int) noexcept { iterator_t ret = *this; ++cursor_; return *this; } /*! * \note * The following are not requirements for input iterator. * We provide them nevertheless. */ //! Pre decrement. iterator_t& operator-- () noexcept { --cursor_; return *this; } //! Post decrement. iterator_t operator-- (int) noexcept { iterator_t ret = *this; --cursor_; return *this; } //! Random access through iterator reference operator[] (difference_type n) noexcept { owner_->get (value_.v_, cursor_ = n); return value_; } //! Random cursor increment iterator_t& operator+= (difference_type n) noexcept { cursor_ += n; return *this; } //! Addition operation iterator_t operator+ (difference_type n) const noexcept { return iterator_t (owner_, cursor_ + n); } //! Random cursor decrement iterator_t& operator-= (difference_type n) noexcept { cursor_ -= n; return *this; } //! Subtraction operation iterator_t operator- (difference_type n) const noexcept { return iterator_t (owner_, cursor_ - n); } //! Export container for comparison const container_t* owner () const noexcept { return owner_; } //! Export cursor for comparison const size_t& cursor () const noexcept { return cursor_; } //! Export value for comparison const value_type_t& value () const noexcept { return value_; } //!@} }; /*! * \brief * Equality comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid * \param lhs Left hand site iterator * \param rhs Right hand site iterator * \return True in equality */ template inline bool operator== (const idxdev_it<_Cont, _It_L, _Size>& lhs, const idxdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return ((lhs.cursor() == rhs.cursor()) && (lhs.owner() == rhs.owner())); } /*! * \brief * Inequality comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid * \param lhs Left hand site iterator * \param rhs Right hand site iterator * \return True in inequality */ template inline bool operator!= (const idxdev_it<_Cont, _It_L, _Size>& lhs, const idxdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return ((lhs.cursor() != rhs.cursor()) || (lhs.owner() != rhs.owner())); } /*! * \note * The following are not requirements for input iterator. * Nevertheless we provide them. * \warn * Required: The rhs and lhs MUST be from the same owner or the * resuled is undefined. */ //!@{ template inline bool operator< (const idxdev_it<_Cont, _It_L, _Size>& lhs, const idxdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() < rhs.cursor()); } template inline bool operator<= (const idxdev_it<_Cont, _It_L, _Size>& lhs, const idxdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() <= rhs.cursor()); } template inline bool operator> (const idxdev_it<_Cont, _It_L, _Size>& lhs, const idxdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() > rhs.cursor()); } template inline bool operator>= (const idxdev_it<_Cont, _It_L, _Size>& lhs, const idxdev_it<_Cont, _It_R, _Size>& rhs) noexcept { return (lhs.cursor() >= rhs.cursor()); } //!@} /*! * Index device iterator concept */ //! @{ #if defined _utl_have_concepts template concept bool Idxdev_it = requires (T t, const T ct) { // STL compatibility typename T::value_type; typename T::difference_type; typename T::pointer; typename T::reference; requires same_< typename T::iterator_category, std::input_iterator_tag >::value; {*t} -> typename T::value_type; // is dereferencable {++t} -> T&; // is incrementable {t++} -> T; // Extras {ct.owner()} ->auto&&; {ct.cursor()} -> const size_t; {ct.value()} -> auto&&; }; #else namespace idxdev_it_details { using std::declval; //! Primary template to catch any non SPI interface types template struct is_idxdev_it_ : false_ {}; //! template to catch a proper SPI interface type template struct is_idxdev_it_ < _Tp, void_t < typename T::value_type, typename T::difference_type, typename T::pointer, typename T::reference, use_if_same_t < typename T::iterator_category, std::input_iterator_tag > > > : true_ {}; } /*! * Value meta-programming function for SPI interface checking * \param _Tp Type to check * \return True if _Tp is a spi interface */ template constexpr bool Idxdev_it = idxdev_it_details::is_idxdev_it_<_Tp>::value; #endif //! @} } //!@} #endif /* __utl_dev_dev_iterators_h__ */