/*!
* \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__ */