/*! * \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 Device iterator collection. */ //!@{ /*! * \brief Traits class for dev_iterators. * * This class does nothing but define nested typedefs. The general * version simply @a forwards the nested typedefs from the Iterator * argument. */ template struct dev_iterator_traits { using iterator_category = _Cat; using value_type = _Tp; using difference_type = _Diff; using pointer = _Tp*; using reference = _Tp&; }; /*! * \brief Iterator tags [std.iterator.tags] * Extension: contiguous_iterator_tag for denoting contiguous iterators. */ struct output_iterator_tag {}; struct input_iterator_tag {}; struct forward_iterator_tag : input_iterator_tag {}; struct bidirectional_iterator_tag : forward_iterator_tag {}; struct random_access_iterator_tag : bidirectional_iterator_tag {}; struct contiguous_iterator_tag : random_access_iterator_tag {}; /* * ================ Output device Iterator ================= */ template class ostreamdev_it { using iterator_t = ostreamdev_it ; //! iterator type local name public: using type = iterator_t; //!< Export type as identity meta-function //! 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; //!@} private: cont_t* owner_ {nullptr}; /*!< * Pointer to parent/owner device class. Usable iterator demands * owner container in order to access data. Considering the data * don't "live" in memory. A default constructed iterator will behave * like end() just like the input version does. */ /*! * \name Constructor / Destructor */ //!@{ public: //! \brief Basic constructor ostreamdev_it (cont_t* owner =nullptr) noexcept : owner_ {owner} { } //! \brief Basic copy constructor ostreamdev_it (const iterator_t& it) noexcept : owner_ {const_cast(it.owner_)} { } //! \brief Basic copy assignment operator iterator_t& operator= (const iterator_t& it) noexcept { owner_ = const_cast(it.owner_); return *this; } //!@} //!@{ \name Public interface public: iterator_t& operator* () noexcept { return *this; } /*! * \brief * Value-assignment operation. Where the output method is invoked * \param value An instance of Cont_t::data_type * \return This %iterator, for chained operations. */ iterator_t& operator= (const value_type& value) { if (owner_ != nullptr) owner_->put (value); return *this; } iterator_t& operator++ () noexcept { return *this; } iterator_t& operator++ (int) noexcept { return *this; } //!@} }; template class istreamdev_it { using iterator_t = istreamdev_it ; //! iterator type local name public: using type = iterator_t; //!< Export type as identity meta-function //! 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; //!@} //! #define-like enumerator for Cursor-like behavior enum Cursor { init = 0, valid = 1, eos = 2 }; //!@{ \name Data members private: cont_t* owner_ {nullptr}; /*!< * Pointer to parent/owner device class. Usable iterator demands * owner container in order to access data. Considering the data * don't "live" in memory. */ value_type value_ {}; Cursor cursor_ {init}; //!@} /*! * \name Constructor / Destructor */ //!@{ public: //! \brief Basic constructor istreamdev_it (cont_t* owner =nullptr, Cursor cursor =eos) noexcept : owner_ {owner}, value_ {}, cursor_{cursor} { } //! \brief Basic copy constructor istreamdev_it (const iterator_t& it) noexcept : owner_ {const_cast(it.owner_)}, value_ {it.value_}, cursor_ {it.cursor_} { } //! \brief Basic copy assignment operator iterator_t& operator= (const iterator_t& it) noexcept { owner_ = const_cast(it.owner_); value_ = it.value_; cursor_ = it.cursor_; return *this; } //!@} //!@{ \name Public interface public: value_type& operator* () noexcept { if (cursor_ == init) ++*this; return value_; } value_type* operator->() noexcept { if (cursor_ == init) ++*this; return &value_; } iterator_t& operator++ () noexcept { _get(value_); return *this; } iterator_t operator++ (int) noexcept { iterator_t r = *this; _get(value_); return r; } //! Export container for comparison const cont_t* owner () const noexcept { return owner_; } //! Export value for comparison const value_type& value () const noexcept { return value_; } //! Export cursor for comparison const Cursor cursor () const noexcept { return cursor_; } //!@} //!@{ \name private api private: void _get(value_type& v) { owner_->get(v); cursor_ = (v) ? valid : eos; } //!@} }; /*! * \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 istreamdev_it<_C1, _D1>& lhs, const istreamdev_it<_C2, _D2>& rhs) noexcept { return ((lhs.owner() == rhs.owner()) && (lhs.value() == rhs.value()) && (lhs.cursor() == rhs.cursor())); } template inline bool operator!= (const istreamdev_it<_C1, _D1>& lhs, const istreamdev_it<_C2, _D2>& rhs) noexcept { return !operator==(lhs, rhs); } /*! * \brief * Output device iterator type. We "future call" interface methods * from owner class to provide iterator functionality. * \param cont_t Container/parent type * \param iter_t Iterator data type (pointer to container_t::value_type) * \param devsize Device's address space size */ template(-1)> class outdev_it { //! iterator type local name using iterator_t = outdev_it ; public: using type = iterator_t; //!< Export type as identity meta-function //! 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; //!@} //! #define-like enumerator for Cursor enum Cursor { beg = _beg, //!< Points the first item (relative address) eod = _end, //!< Points one place after last item (relative address) }; private: cont_t* owner_ {nullptr}; /*!< * Pointer to parent/owner device class. Usable iterator demands * owner container in order to access data. Considering the data * don't "live" in memory. A default constructed iterator will behave * like end() just like the input version does. */ index_t cursor_ {eod}; //!< virtual cursor for comparison operators /*! * \name Constructor / Destructor */ //!@{ public: //! \brief Default constructor results to end() explicit outdev_it() noexcept : owner_ {nullptr}, cursor_{eod} { } //! \brief Basic constructor explicit outdev_it (cont_t* owner, index_t cursor =eod) 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 Basic copy assignment operator iterator_t& operator= (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()} { } //!@} //!@{ \name Public interface public: iterator_t& operator* () noexcept { return *this; } /*! * \brief * Value-assignment operation. Where the output method is invoked * \param value An instance of Cont_t::data_type * \return This %iterator, for chained operations. */ iterator_t& operator= (const value_type& value) { // end() and default constructible iterators are not dereferenceable if (cursor_ != eod) owner_->put (value, cursor_); return *this; } //!@{ \name ++ operators iterator_t& operator++ () noexcept { ++cursor_; return *this; } iterator_t operator++ (int) noexcept { iterator_t ret = *this; ++cursor_; return ret; } //!@} //! Export container for comparison const cont_t* owner () const noexcept { return owner_; } //! Export cursor for comparison const index_t cursor () const noexcept { return cursor_; } //!@} }; /*! * \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. */ //!@{ /*! * \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<_Cont1, _It1, _beg1, _end1>& lhs, const outdev_it<_Cont2, _It2, _beg2, _end2>& 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<_Cont1, _It1, _beg1, _end1>& lhs, const outdev_it<_Cont2, _It2, _beg2, _end2>& rhs) noexcept { return !(lhs == rhs); } //!@} /*! * 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_ : meta::false_ {}; //! template to catch a proper SPI interface type template struct is_outdev_it_ < _Tp, meta::void_t < typename _Tp::value_type, typename _Tp::difference_type, typename _Tp::pointer, typename _Tp::reference, meta::use_if_same_t < typename _Tp::iterator_category, std::output_iterator_tag > > > : meta::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 cont_t Container/parent type * \param iter_t Iterator data type (pointer to container_t::value_type) * \param _beg Device starting address * \param _size Device's address space size */ template(-1)-_beg> class indev_it { //!< iterator type local name using iterator_t = indev_it ; public: using type = iterator_t; //!< Export type as identity meta-function //! 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; //!@} //! #define-like enumerator for Cursor enum Cursor { beg = _beg, //!< Points the first item eod = _beg + _size, //!< Points one place after last item }; private: cont_t* owner_{nullptr}; /*!< * Pointer to parent/owner device class. Constructor demands * owner container in order to access data. Considering the data * don't "live" in memory. */ index_t cursor_{eod}; //!< virtual cursor for comparison operators value_type value_ {}; //!< The current value, used as cache /*! * \name Constructor / Destructor * \note * We can not provide a default constructor as long as we depend * on container_t (the owner type). */ //!@{ public: //! \brief Default constructor indev_it () noexcept : owner_ {nullptr}, cursor_{eod}, value_ {} { } //! \brief Basic constructor explicit indev_it (cont_t* own, size_t cur =eod) 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 Basic copy assignment operator iterator_t& operator= (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()} { } //!@} //!@{ \name Public interface public: //! De-reference operator. No end() place dereference check is made. reference operator* () { owner_->get (value_, cursor_); return value_; } //! Arrow operator. No end() place dereference check is made. pointer operator-> () { owner_->get (value_, cursor_); return &value_; } //! Pre increment. iterator_t& operator++ () { ++cursor_; return *this; } //! Post increment. iterator_t operator++ (int) { iterator_t ret(*this); ++cursor_; return *this; } //! Pre decrement. iterator_t& operator-- () { --cursor_; return *this; } //! Post decrement. iterator_t operator-- (int) { iterator_t ret(*this); --cursor_; return *this; } //! [] operator. Is a combination of input method and dereference reference operator[] (difference_type n) { owner_->get (value_, cursor_ += n); return value_; } iterator_t& operator+= (difference_type n) { cursor_ += n; return *this; } iterator_t operator+ (difference_type n) { return iterator_t (owner_, cursor_ + n); } iterator_t& operator-= (difference_type n) { cursor_ -= n; return *this; } iterator_t operator- (difference_type n) { return iterator_t (owner_, cursor_ - n); } //! Export container for comparison const cont_t* owner () const noexcept { return owner_; } //! Export cursor for comparison const index_t cursor () const noexcept { return cursor_; } //! Export value for comparison //const reference value () const noexcept { return value_; } //!@} }; /*! * \name indev_it EqualityComparable && LessThanComparable requirements * comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid */ //!@{ //! Equality template inline bool operator== (const indev_it<_Cont1, _It1, _beg1, _size1>& x, const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return (x.owner() == y.owner() && (x.cursor() == y.cursor())); } //! Less than template inline bool operator< (const indev_it<_Cont1, _It1, _beg1, _size1>& x, const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return (x.cursor() < y.cursor()); } // relative ops template inline bool operator!= (const indev_it<_Cont1, _It1, _beg1, _size1>& x, const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return !(x == y); } template inline bool operator<= (const indev_it<_Cont1, _It1, _beg1, _size1>& x, const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return !(y < x); } template inline bool operator> (const indev_it<_Cont1, _It1, _beg1, _size1>& x, const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return (y < x); } template inline bool operator>= (const indev_it<_Cont1, _It1, _beg1, _size1>& x, const indev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return !(x < y); } //!@} //! @{ \name iterator arithmetic template inline auto operator- (indev_it<_Cont1, _It1, _beg1, _size1>& x, indev_it<_Cont1, _It1, _beg1, _size1>& y) noexcept -> decltype (x.cursor() - y.cursor()) { return (x.cursor() - y.cursor()); } template inline indev_it<_Cont, _It, _beg, _size> operator+ (typename _Cont::difference_type n, indev_it<_Cont, _It, _beg, _size>& it) noexcept { return indev_it<_Cont, _It, _beg, _size>(it.owner(), it.cursor() + n); } //! @} /*! * 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_ : meta::false_ {}; //! template to catch a proper SPI interface type template struct is_indev_it_ < _Tp, meta::void_t < typename _Tp::value_type, typename _Tp::difference_type, typename _Tp::pointer, typename _Tp::reference, meta::use_if_same_t < typename _Tp::iterator_category, std::input_iterator_tag > > > : meta::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 ================= */ /*! * \brief * Indexed device iterator type. We "future call" interface methods * from owner class to provide iterator functionality. * \note * This is a contiguous iterator * \param cont_t Container/parent type * \param iter_t Iterator data type (pointer to container_t::value_type) * \param _beg Starting address of the device * \param _size Device's address space size */ template(-1)-_beg> class iodev_it { //!< iterator type local name using iterator_t = iodev_it ; public: using type = iterator_t; //!< Export type as identity meta-function //! 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; //!@} //! #define-like enumerator for Cursor enum Cursor { beg = _beg, //!< Points the first item eod = _beg+_size, //!< Points one place after last item }; private: cont_t* owner_{nullptr}; /*!< * Pointer to parent/owner device class. Constructor demands * owner container in order to access data. Considering the data * don't "live" in memory. */ index_t cursor_{eod}; //!< virtual cursor for comparison operators /*! * Current value wrapper type, to used as put event catcher * owner_->get : Use v_ directly * owner_->put : Use operator= to a dereferenced iterator */ struct value_type_t { value_type v_; //!< Current value buffer to access via get value_type_t(value_type v =value_type{}) : v_{v} { } operator value_type() { return v_; } operator reference() { return v_; } value_type& operator= (const value_type& v) { //!< Catch any attempt to write to value_ and sync the data back to device owner_->put (v_ =v, cursor_); } } value_ {}; /*! * \name Constructor / Destructor */ //!@{ public: //! \brief Default constructor iodev_it () noexcept : owner_ {nullptr}, cursor_{eod}, value_ {} { } //! \brief Basic constructor explicit iodev_it (cont_t* owner, size_t cursor =eod) noexcept : owner_ {owner}, cursor_{cursor}, value_ {} { } //! \brief Basic copy constructor explicit iodev_it (const iterator_t& 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; } //! 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 cont_t* owner () const noexcept { return owner_; } //! Export cursor for comparison const index_t& cursor () const noexcept { return cursor_; } //! Export value for comparison //const value_type_t& value () const noexcept { return value_; } //!@} }; /*! * \name indev_it EqualityComparable && LessThanComparable requirements * comparison template so that comparison between cv-qualified and * non-cv-qualified iterators be valid */ //!@{ //! Equality template inline bool operator== (const iodev_it<_Cont1, _It1, _beg1, _size1>& x, const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return (x.owner() == y.owner() && (x.cursor() == y.cursor())); } //! Less than template inline bool operator< (const iodev_it<_Cont1, _It1, _beg1, _size1>& x, const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return (x.cursor() < y.cursor()); } // relative ops template inline bool operator!= (const iodev_it<_Cont1, _It1, _beg1, _size1>& x, const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return !(x == y); } template inline bool operator<= (const iodev_it<_Cont1, _It1, _beg1, _size1>& x, const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return !(y < x); } template inline bool operator> (const iodev_it<_Cont1, _It1, _beg1, _size1>& x, const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return (y < x); } template inline bool operator>= (const iodev_it<_Cont1, _It1, _beg1, _size1>& x, const iodev_it<_Cont2, _It2, _beg2, _size2>& y) noexcept { return !(x < y); } //!@} //! @{ \name iterator arithmetic template inline auto operator- (iodev_it<_Cont1, _It1, _beg1, _size1>& x, iodev_it<_Cont1, _It1, _beg1, _size1>& y) noexcept -> decltype (x.cursor() - y.cursor()) { return (x.cursor() - y.cursor()); } template inline iodev_it<_Cont, _It, _beg, _size> operator+ (typename _Cont::difference_type n, iodev_it<_Cont, _It, _beg, _size>& it) noexcept { return iodev_it<_Cont, _It, _beg, _size>(it.owner(), it.cursor() + n); } //! @} /*! * 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_ : meta::false_ {}; //! template to catch a proper SPI interface type template struct is_idxdev_it_ < _Tp, meta::void_t < typename _Tp::value_type, typename _Tp::difference_type, typename _Tp::pointer, typename _Tp::reference, meta::use_if_same_t < typename _Tp::iterator_category, std::input_iterator_tag > > > : meta::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__ */