diff --git a/include/utl/dev/dev_iterators.h b/include/utl/dev/dev_iterators.h index e417730..51e682a 100644 --- a/include/utl/dev/dev_iterators.h +++ b/include/utl/dev/dev_iterators.h @@ -21,62 +21,237 @@ #ifndef __utl_dev_dev_iterators_h__ #define __utl_dev_dev_iterators_h__ -#include -#include +#include +#include namespace utl { /*! * \ingroup Devices - * \brief Iterator collection. + * \brief Device iterator collection. */ //!@{ /*! - * Common template Iterator trait dispatcher for the STL requirement - * type definitions. - * Our iterators inherit from specializations of this type + * \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 { }; - - //! 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; + struct dev_iterator_traits { + using iterator_category = _Cat; + using value_type = _Tp; + using difference_type = _Diff; + using pointer = _Tp*; + using reference = _Tp&; }; - //! 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; - }; + /*! + * \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 container_t Container/parent type - * \param iter_t Iterator data type (pointer to container_t::value_type) - * \param streamsize Stream size + * \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 + template(-1)> class outdev_it { //! iterator type local name - using iterator_t = outdev_it ; - + using iterator_t = outdev_it ; + public: + using type = iterator_t; //!< Export type as identity meta-function //! STL iterator traits "forwarding" //!@{ public: @@ -86,51 +261,51 @@ namespace utl { 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 + beg = _beg, //!< Points the first item (relative address) + eod = _end, //!< Points one place after last item (relative address) }; private: - container_t* owner_; /*!< - * Pointer to parent/owner device class. Constructor demands + 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. + * don't "live" in memory. A default constructed iterator will behave + * like end() just like the input version does. */ - size_t cursor_ {beg}; //!< virtual cursor for comparison operators + index_t cursor_ {eod}; //!< 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 Default constructor results to end() + explicit outdev_it() noexcept : + owner_ {nullptr}, + cursor_{eod} { } //! \brief Basic constructor - explicit outdev_it (container_t* owner, size_t cursor =beg) noexcept - : owner_{owner}, - cursor_{cursor} { } + 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 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()} { } + 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(); + 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()} { } //!@} @@ -139,40 +314,23 @@ namespace utl { iterator_t& operator* () noexcept { return *this; } /*! * \brief - * Assignment operation. Where the output method is invoked - * \param value An instance of container_t basic type + * 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) { - owner_->put (value); + // end() and default constructible iterators are not dereferenceable + if (cursor_ != eod) + owner_->put (value, cursor_); 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 { + //!@{ \name ++ operators + iterator_t& operator++ () noexcept { ++cursor_; return *this; } - template - use_if_t <(S != 0), iterator_t> operator++ (int) noexcept { + iterator_t operator++ (int) noexcept { iterator_t ret = *this; ++cursor_; return ret; @@ -180,12 +338,21 @@ namespace utl { //!@} //! Export container for comparison - const container_t* owner () const noexcept { return owner_; } + const cont_t* owner () const noexcept { return owner_; } //! Export cursor for comparison - const size_t cursor () const noexcept { return cursor_; } + 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 @@ -194,9 +361,10 @@ namespace utl { * \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 { + 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())); } @@ -209,44 +377,12 @@ namespace utl { * \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 { + template + inline bool operator!= (const outdev_it<_Cont1, _It1, _beg1, _end1>& lhs, + const outdev_it<_Cont2, _It2, _beg2, _end2>& 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()); - } //!@} /*! @@ -315,39 +451,41 @@ namespace utl { * \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 + * \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 + template(-1)-_beg> class indev_it { - using iterator_t = indev_it ; //!< iterator type local name + //!< 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; + 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 + beg = _beg, //!< Points the first item + eod = _beg + _size, //!< 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_ {init}; //!< virtual cursor for comparison operators - value_type value_ {}; //!< The current value, used as a buffer + 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 @@ -357,143 +495,171 @@ namespace utl { */ //!@{ public: - //! \brief Basic constructor - explicit indev_it (container_t* own, size_t cur =init) noexcept - : owner_{own}, - cursor_{cur}, - value_{} { } + //! \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 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()} { } + 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(); + 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. 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; + //! De-reference operator. No end() place dereference check is made. + reference operator* () { + owner_->get (value_, cursor_); return value_; } - //! Arrow operator - pointer operator-> () noexcept { - if (cursor_ == init) - ++*this; + //! Arrow operator. No end() place dereference check is made. + pointer operator-> () { + owner_->get (value_, cursor_); return &value_; } - //! Pre increment. This is where the input method is invoked - iterator_t& operator++ () noexcept { - owner_->get (value_); + + //! Pre increment. + iterator_t& operator++ () { ++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_); + //! 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 container_t* owner () const noexcept { return owner_; } + const cont_t* owner () const noexcept { return owner_; } //! Export cursor for comparison - const size_t cursor () const noexcept { return cursor_; } + const index_t cursor () const noexcept { return cursor_; } //! Export value for comparison - const value_type& value () const noexcept { return value_; } + //const reference 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 + * \name indev_it EqualityComparable && LessThanComparable requirements + * comparison template so that comparison between cv-qualified and + * non-cv-qualified iterators be valid */ - 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())); + //!@{ + //! 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())); } - - /*! - * \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())); + //! 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()); } - /*! - * \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()); + // 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<_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<_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); } + //!@} - 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()); + //! @{ \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 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 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 @@ -554,7 +720,7 @@ namespace utl { //! @} /* - * ================= Indexed device Iterator (input) ================= + * ================= Indexed device Iterator ================= */ /*! @@ -562,90 +728,82 @@ namespace utl { * 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 + * 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 - class idxdev_it { - using iterator_t = idxdev_it ; //!< iterator type local name - - //!@{ STL iterator traits "forwarding" + template(-1)-_beg> + class iodev_it { + //!< iterator type local name + using iterator_t = iodev_it ; 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 + //! 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 + //! #define-like enumerator for Cursor enum Cursor { - beg = 0, //!< Points the first item - eos = N, //!< Points one place after last item + beg = _beg, //!< Points the first item + eod = _beg+_size, //!< 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 + 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= + * 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 =0) : v_{v} { } + 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) { - v_ = v; //!< Catch any attempt to write to value_ - owner_->put (v_, cursor_); //!< Sync the data back to device + //!< Catch any attempt to write to value_ and sync the data back to device + owner_->put (v_ =v, cursor_); } } 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 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 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()} { - - } + 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(); + owner_ = const_cast(it.owner_); + cursor_ = it.cursor_; + value_ = it.value_; } //!@} @@ -677,12 +835,6 @@ namespace utl { ++cursor_; return *this; } - - /*! - * \note - * The following are not requirements for input iterator. - * We provide them nevertheless. - */ //! Pre decrement. iterator_t& operator-- () noexcept { --cursor_; @@ -719,77 +871,79 @@ namespace utl { } //! Export container for comparison - const container_t* owner () const noexcept { return owner_; } + const cont_t* owner () const noexcept { return owner_; } //! Export cursor for comparison - const size_t& cursor () const noexcept { return cursor_; } + const index_t& cursor () const noexcept { return cursor_; } //! Export value for comparison - const value_type_t& value () const noexcept { return value_; } + //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 + * \name indev_it EqualityComparable && LessThanComparable requirements + * comparison template so that comparison between cv-qualified and + * non-cv-qualified iterators be valid */ - 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())); + //!@{ + //! 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())); } - - /*! - * \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())); + //! 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()); } - /*! - * \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()); + // 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 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 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); + } + //!@} - 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()); + //! @{ \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 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 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 diff --git a/include/utl/dev/ostream_dev.h b/include/utl/dev/ostream_dev.h new file mode 100644 index 0000000..c5cbd2a --- /dev/null +++ b/include/utl/dev/ostream_dev.h @@ -0,0 +1,286 @@ +/*! + * \file utl/dev/ostream_dev.h + * \brief Abstract base class interface for output devices of utl. + * + * 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_ostream_dev_h__ +#define __utl_dev_ostream_dev_h__ + +#include +#include +#include +#include + +namespace utl { + +/*! + * \ingroup Device Interface + * \brief Abstract base classes for output stream devices + */ +//!@{ + + /*! + * \brief + * Template base class for output stream devices using CRTP + * + * This class force a common interface for output stream devices. + * By using this common interface the class implements + * - Stream-like inserting operator + * - Output iterator + * - Const output iterator + * to inherit to implementation. + * + * \param impl_t The CRTP type (the derived/implementation class typename). + * \param data_t The devices base type of data + */ + template + class ostream_dev { + _CRTP_IMPL(impl_t); + using ostream_dev_t = ostream_dev ; //!< class type syntactic sugar + + //! Export types as output device concept demands + //! @{ + public: + using data_type = data_t; + using pointer_type = data_t*; + //!@} + using type = ostream_dev_t; //!< Export type as identity meta-function + /*! + * \name Constructor / Destructor + */ + //!@{ + protected: + ~ostream_dev () = default; //!< \brief Allow destructor from derived only + ostream_dev () = default; //!< \brief A default constructor from derived only + ostream_dev(const ostream_dev_t&) = delete; //!< No copies + ostream_dev_t& operator= (const ostream_dev_t&) = delete; //!< No copy assignments + //!@} + + //! \name Common output device interface requirements + //!@{ + private: + size_t put_ (const data_t& data) { return impl().put_ (data); } + size_t put_ (const data_t* data, size_t n) { + return impl().put_ (data, n); + } + //!@} + /*! + * \name Common output device interface + */ + //!@{ + public: + /*! + * \brief + * Put interface. This function should send a single + * data_t object to device. + * \param data The data to send + * \return The number of transmitted data items + * \note + * A successful call should return 1 + */ + size_t put (const data_t& data) { + return put_ (data); + } + + /*! + * \brief + * Put interface. This function should send a stream of + * data_t objects to device. + * \param data Pointer to buffer indenting write to device. + * \param n The number of data of type data_t to send + * \return The number of transmitted items. + */ + size_t put (const data_t* data, size_t n) { + return put_ (data, n); + } + //!@} + + /*! + * \name Stream operator << interface + */ + //!@{ + public: + /*! + * \brief + * Template operator<< implementation for for all by value/ref parameters + * \note + * In the case _Src_t size is not an exact multiple of data_t size + * the write data will be truncated and there may be data loss. + * \param src Reference to source data + * \return Reference to this device for chaining + */ + template + ostream_dev_t& operator<< (const _Src_t& src) { + static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0), + "Source size must be an integer multiple of device's data size"); + put_ (reinterpret_cast(&src), sizeof(_Src_t)/sizeof(data_t)); + return *this; + } + //! Overload to disallow pointer types as source + template + ostream_dev_t& operator<< (_Src_t* src) = delete; + + //! Overload for single data_t object + ostream_dev_t& operator<< (const data_t& src) { + put_ (src); + return *this; + } + //ToDo: Add support for c-string, utl::string, ... + //!@} + + /*! + * \name STL-like Output iterator interface + */ + //!@{ + using iterator = ostreamdev_it ; //!< Iterator + using const_iterator = ostreamdev_it ; //!< Const iterator + + //!@{ .begin implementation + iterator begin () noexcept { return iterator(this); } + const_iterator begin () const noexcept { return const_iterator(this); } + const_iterator cbegin () const noexcept { return const_iterator(this); } + //!@} + //!@{ .end implementation + iterator end () noexcept { return iterator(); } + const_iterator end () const noexcept { return const_iterator(); } + const_iterator cend () const noexcept { return const_iterator(); } + //!@} + //!@} + }; + + + + + + template + class ostream_dev { + using ostream_dev_t = ostream_dev ; //!< class type syntactic sugar + + //! Export types as output device concept demands + //! @{ + public: + using data_type = data_t; + using pointer_type = data_t*; + //!@} + using type = ostream_dev_t; //!< Export type as identity meta-function + /*! + * \name Constructor / Destructor + */ + //!@{ + public: + virtual ~ostream_dev () = default; //!< \brief Virtual destructor + protected: + ostream_dev () = default; //!< \brief A default constructor from derived only + ostream_dev(const ostream_dev_t&) = delete; //!< No copies + ostream_dev_t& operator= (const ostream_dev_t&) = delete; //!< No copy assignments + //!@} + + //! \name Common output device interface requirements + //!@{ + private: + virtual size_t put_ (const data_t& data) =0; + virtual size_t put_ (const data_t* data, size_t n) =0; + //!@} + /*! + * \name Common output device interface + */ + //!@{ + public: + /*! + * \brief + * Put interface. This function should send a single + * data_t object to device. + * \param data The data to send + * \return The number of transmitted data items + * \note + * A successful call should return 1 + */ + size_t put (const data_t& data) { + return put_ (data); + } + + /*! + * \brief + * Put interface. This function should send a stream of + * data_t objects to device. + * \param data Pointer to buffer indenting write to device. + * \param n The number of data of type data_t to send + * \return The number of transmitted items. + */ + size_t put (const data_t* data, size_t n) { + return put_ (data, n); + } + //!@} + + /*! + * \name Stream operator << interface + */ + //!@{ + public: + /*! + * \brief + * Template operator<< implementation for for all by value/ref parameters + * \note + * In the case _Src_t size is not an exact multiple of data_t size + * the write data will be truncated and there may be data loss. + * \param src Reference to source data + * \return Reference to this device for chaining + */ + template + ostream_dev_t& operator<< (const _Src_t& src) { + static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0), + "Source size must be an integer multiple of device's data size"); + put_ (reinterpret_cast(&src), sizeof(_Src_t)/sizeof(data_t)); + return *this; + } + //! Overload to disallow pointer types as source + template + ostream_dev_t& operator<< (_Src_t* src) = delete; + + //! Overload for single data_t object + ostream_dev_t& operator<< (const data_t& src) { + put_ (src); + return *this; + } + //!@} + + /*! + * \name STL-like Output iterator interface + */ + //!@{ + using iterator = ostreamdev_it ; //!< Iterator + using const_iterator = ostreamdev_it ; //!< Const iterator + + //!@{ .begin implementation + iterator begin () noexcept { return iterator(this); } + const_iterator begin () const noexcept { return const_iterator(this); } + const_iterator cbegin () const noexcept { return const_iterator(this); } + //!@} + //!@{ .end implementation + iterator end () noexcept { return iterator(); } + const_iterator end () const noexcept { return const_iterator(); } + const_iterator cend () const noexcept { return const_iterator(); } + //!@} + //!@} + }; + + +//!@} +} //namespace utl + +#endif /* #ifndef __utl_dev_out_dev_h__ */ diff --git a/include/utl/dev/out_dev.h b/include/utl/dev/out_dev.h index a86a493..5a1d6d2 100644 --- a/include/utl/dev/out_dev.h +++ b/include/utl/dev/out_dev.h @@ -21,16 +21,16 @@ #ifndef __utl_dev_out_dev_h__ #define __utl_dev_out_dev_h__ -#include -#include -#include +#include +#include #include +#include namespace utl { /*! * \ingroup Device Interface - * \brief Abstract base class for output devices + * \brief Abstract base classes for output devices */ //!@{ @@ -134,7 +134,7 @@ namespace utl { put_ (reinterpret_cast(&src), sizeof(_Src_t)/sizeof(data_t)); return *this; } - //! specialization to disallow pointer types as source + //! Overload to disallow pointer types as source template out_dev_t& operator<< (_Src_t* src) = delete; @@ -150,7 +150,7 @@ namespace utl { */ //!@{ using iterator = outdev_it ; //!< Iterator - using const_iterator = outdev_it ; //!< Const iterator + using const_iterator = outdev_it ; //!< Const iterator //!@{ .begin implementation iterator begin () noexcept { return iterator(this, iterator::beg); } @@ -249,7 +249,7 @@ namespace utl { put_ (reinterpret_cast(&src), sizeof(_Src_t)/sizeof(data_t)); return *this; } - //! specialization to disallow pointer types as source + //! Overload to disallow pointer types as source template out_dev_t& operator<< (_Src_t* src) = delete; @@ -265,7 +265,7 @@ namespace utl { */ //!@{ using iterator = outdev_it ; //!< Iterator - using const_iterator = outdev_it ; //!< Const iterator + using const_iterator = outdev_it ; //!< Const iterator //!@{ .begin implementation iterator begin () noexcept { return iterator(this, iterator::beg); } @@ -289,7 +289,6 @@ namespace utl { template concept bool Out_dev = requires (_Tp t, const _Tp ct, typename _Tp::data_type v) { // Object type - // requires std::is_default_constructible<_Tp>::value; requires !std::is_copy_constructible<_Tp>::value; requires !std::is_copy_assignable<_Tp>::value; // Methods @@ -298,15 +297,15 @@ namespace utl { // Operators t << v; // Iterators - typename _Tp::const_iterator; //XXX: change to concept: is_idxdev_iterator<_Tp> + typename _Tp::const_iterator; requires Outdev_it; -// requires Outdev_it; + requires Outdev_it; { t.begin() } -> typename _Tp::iterator; {ct.begin()} -> typename _Tp::const_iterator; - // { t.cbegin()} -> typename _Tp::const_iterator; + { t.cbegin()} -> typename _Tp::const_iterator; { t.end() } -> typename _Tp::iterator; -// {ct.end()} -> typename _Tp::const_iterator; - // { t.cend()} -> typename _Tp::const_iterator; + {ct.end()} -> typename _Tp::const_iterator; + { t.cend()} -> typename _Tp::const_iterator; }; #else namespace out_dev_details {