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