A first group of device interface base classes added.doc
@@ -0,0 +1,854 @@ | |||||
/*! | |||||
* \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 <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_dev_dev_iterators_h__ | |||||
#define __utl_dev_dev_iterators_h__ | |||||
#include <utl/impl/impl.h> | |||||
#include <utl/meta/sfinae.h> | |||||
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 <typename _Cat, typename _Tp, typename _Diff =ptrdiff_t> | |||||
struct dev_iterator_traits { }; | |||||
//! Partial specialization for pointer types. | |||||
template<typename _Cat, typename _Tp, typename _Diff> | |||||
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<typename _Cat, typename _Tp, typename _Diff> | |||||
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<typename container_t, typename iter_t, size_t streamsize =0> | |||||
class outdev_it { | |||||
//! iterator type local name | |||||
using iterator_t = outdev_it <container_t, iter_t, streamsize>; | |||||
//! STL iterator traits "forwarding" | |||||
//!@{ | |||||
public: | |||||
using iterator_category = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::iterator_category; | |||||
using value_type = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::value_type; | |||||
using difference_type = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::difference_type; | |||||
using pointer = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::pointer; | |||||
using reference = typename dev_iterator_traits <std::output_iterator_tag, iter_t>::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<container_t*>(it.owner())}, | |||||
cursor_ {it.cursor()} { } | |||||
//! \brief Iterator to const-iterator conversion (as STL requires) | |||||
//! \param it Iterator reference | |||||
template<typename _It> | |||||
outdev_it (const outdev_it< | |||||
use_if_same_t <_It, typename container_t::pointer_type, container_t>, | |||||
_It, | |||||
streamsize | |||||
>& it) noexcept | |||||
: owner_ {const_cast<container_t*>(it.owner())}, | |||||
cursor_ {it.cursor()} { } | |||||
//! \brief Basic copy assignment operator | |||||
iterator_t& operator= (const iterator_t& it) noexcept { | |||||
owner_ = const_cast<container_t*>(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 <size_t S =streamsize> | |||||
use_if_t <(S == 0), iterator_t&> operator++ () noexcept { *this; } | |||||
template <size_t S =streamsize> | |||||
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 <size_t S =streamsize> | |||||
use_if_t <(S != 0), iterator_t&> operator++ () noexcept { | |||||
++cursor_; | |||||
return *this; | |||||
} | |||||
template <size_t S =streamsize> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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 <typename T> | |||||
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 <typename _Tp, typename =void> | |||||
struct is_outdev_it_ : false_ {}; | |||||
//! template to catch a proper SPI interface type | |||||
template <typename _Tp> | |||||
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 <typename _Tp> | |||||
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<typename container_t, typename iter_t, size_t streamsize> | |||||
class indev_it { | |||||
using iterator_t = indev_it <container_t, iter_t, streamsize>; //!< iterator type local name | |||||
//! STL iterator traits "forwarding" | |||||
//!@{ | |||||
public: | |||||
using iterator_category = typename dev_iterator_traits <std::input_iterator_tag, iter_t>::iterator_category; | |||||
using value_type = typename dev_iterator_traits <std::input_iterator_tag, iter_t>::value_type; | |||||
using difference_type = typename dev_iterator_traits <std::input_iterator_tag, iter_t>::difference_type; | |||||
using pointer = typename dev_iterator_traits <std::input_iterator_tag, iter_t>::pointer; | |||||
using reference = typename dev_iterator_traits <std::input_iterator_tag, iter_t>::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<container_t*>(it.owner())}, | |||||
cursor_{it.cursor()}, | |||||
value_ {it.value()} { } | |||||
//! \brief Iterator to const-iterator conversion (as STL requires) | |||||
//! \param it Iterator reference | |||||
template<typename _It> | |||||
indev_it (const indev_it< | |||||
use_if_same_t <_It, typename container_t::pointer_type, container_t>, | |||||
_It, | |||||
streamsize | |||||
>& it) noexcept | |||||
: owner_ {const_cast<container_t*>(it.owner())}, | |||||
cursor_{it.cursor()}, | |||||
value_ {it.value()} { } | |||||
//! \brief Basic copy assignment operator | |||||
iterator_t& operator= (const iterator_t& it) noexcept { | |||||
owner_ = const_cast<container_t*>(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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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 <typename T> | |||||
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 <typename _Tp, typename =void> | |||||
struct is_indev_it_ : false_ {}; | |||||
//! template to catch a proper SPI interface type | |||||
template <typename _Tp> | |||||
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 <typename _Tp> | |||||
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<typename container_t, typename iter_t, size_t N> | |||||
class idxdev_it { | |||||
using iterator_t = idxdev_it <container_t, iter_t, N>; //!< iterator type local name | |||||
//!@{ STL iterator traits "forwarding" | |||||
public: | |||||
using iterator_category = typename dev_iterator_traits <std::input_iterator_tag, iter_t, size_t>::iterator_category; | |||||
using value_type = typename dev_iterator_traits <std::input_iterator_tag, iter_t, size_t>::value_type; | |||||
using difference_type = typename dev_iterator_traits <std::input_iterator_tag, iter_t, size_t>::difference_type; | |||||
using pointer = typename dev_iterator_traits <std::input_iterator_tag, iter_t, size_t>::pointer; | |||||
using reference = typename dev_iterator_traits <std::input_iterator_tag, iter_t, size_t>::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<container_t*> (it.owner())}, | |||||
cursor_{it.cursor()}, | |||||
value_ {it.value()} { } | |||||
//! \brief Iterator to const iterator conversion (as STL requires) | |||||
//! \param it Iterator reference | |||||
template<typename _It> | |||||
idxdev_it (const idxdev_it< | |||||
use_if_same_t <_It, typename container_t::pointer_type, container_t>, | |||||
_It, | |||||
N | |||||
>& it) noexcept | |||||
: owner_ {const_cast<container_t*> (it.owner())}, | |||||
cursor_{it.cursor()}, | |||||
value_ {it.value()} { | |||||
} | |||||
//! \brief Basic copy assignment operator | |||||
iterator_t& operator= (const iterator_t& it) noexcept { | |||||
owner_ = const_cast<container_t*>(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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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<typename _Cont, typename _It_L, typename _It_R, size_t _Size> | |||||
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 <typename T> | |||||
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 <typename _Tp, typename =void> | |||||
struct is_idxdev_it_ : false_ {}; | |||||
//! template to catch a proper SPI interface type | |||||
template <typename _Tp> | |||||
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 <typename _Tp> | |||||
constexpr bool Idxdev_it = idxdev_it_details::is_idxdev_it_<_Tp>::value; | |||||
#endif | |||||
//! @} | |||||
} | |||||
//!@} | |||||
#endif /* __utl_dev_dev_iterators_h__ */ |
@@ -0,0 +1,530 @@ | |||||
/*! | |||||
* \file utl/dev/idx_dev.h | |||||
* \brief Abstract base class implementations for indexed | |||||
* devices interface 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 <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_dev_idx_dev_h__ | |||||
#define __utl_dev_idx_dev_h__ | |||||
#include <utl/impl/impl.h> | |||||
#include <utl/helper/crtp.h> | |||||
#include <utl/meta/sfinae.h> | |||||
#include <utl/dev/dev_iterators.h> | |||||
namespace utl { | |||||
/*! | |||||
* \ingroup Device Interface | |||||
* \brief Abstract base class for indexed devices | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template base class for indexed (I/O) devices. using CRTP | |||||
* | |||||
* This class force a common interface for input, indexed (I/O) devices. | |||||
* By using this common interface the class implements | |||||
* - Stream-like extracting and insertion operator | |||||
* - Helper operators | |||||
* - Input iterator | |||||
* - Const input 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 | |||||
* \param idx_t The type to use for indexing | |||||
* \param streamsize The number of elements to indicate eos. | |||||
*/ | |||||
template <typename impl_t, typename data_t, typename idx_t, size_t N> | |||||
class idx_dev { | |||||
_CRTP_IMPL(impl_t); | |||||
using idx_dev_t = idx_dev <impl_t, data_t, idx_t, N>; //!< class type syntactic sugar | |||||
//!@{ Export types as index device concept demands | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type= data_t*; | |||||
using idx_type = idx_t; | |||||
//!@} | |||||
using type = idx_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
protected: | |||||
~idx_dev () = default; //!< \brief Allow destructor from derived only | |||||
idx_dev () = default; //!< \brief A default constructor from derived only | |||||
idx_dev(const idx_dev_t&) = delete; //!< No copies | |||||
idx_dev_t& operator= (const idx_dev_t&) = delete; //!< No copy assignments | |||||
//!@} | |||||
//! \name Common index device interface requirements | |||||
//!@{ | |||||
private: | |||||
size_t get_ (data_t& data, idx_t idx) { return impl().get_(data, idx); } | |||||
size_t get_ (data_t* data, size_t n, idx_t idx) { return impl().get_(data, n, idx); } | |||||
size_t put_ (const data_t& data, idx_t idx) { return impl().put_(data, idx); } | |||||
size_t put_ (const data_t* data, size_t n, idx_t idx) { return impl().put_ (data, n, idx); } | |||||
idx_t cursor_ () const { return impl().cursor_(); } | |||||
void cursor_ (idx_t idx) { impl().cursor_(idx); } | |||||
//!@} | |||||
/*! | |||||
* \name Public index device interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
/*! | |||||
* \brief | |||||
* Get interface. This function should read | |||||
* a single data_t object from device in blocking mode. | |||||
* \param data Reference to data output from device. | |||||
* \return Number of data read from device | |||||
* \note | |||||
* A successful call should return 1 | |||||
*/ | |||||
size_t get (data_t& data, idx_t cursor) { | |||||
return get_ (data, cursor); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Old stile get functionality using free standing data_t*. | |||||
* This function should return a stream of data from device | |||||
* \param data Pointer to buffer to write the data from device. | |||||
* \param n The number of data of type data_t to read | |||||
* \return The read data items. | |||||
*/ | |||||
size_t get (data_t* data, size_t n, idx_t cursor) { | |||||
return get_ (data, n, cursor); | |||||
} | |||||
/*! | |||||
* \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, idx_t cursor) { | |||||
return put_ (data, cursor); | |||||
} | |||||
/*! | |||||
* \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, idx_t cursor) { | |||||
return put_ (data, n, cursor); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Return the current cursor position | |||||
*/ | |||||
idx_t cursor () const { return cursor_(); } | |||||
/*! | |||||
* \brief | |||||
* Set the cursor position | |||||
* \param idx Cursor address to set | |||||
* \return The current cursor | |||||
*/ | |||||
idx_t cursor (idx_t idx) { return cursor_(idx); } | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator >> interface | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template operator>> implementation for for all by value/ref parameters | |||||
* \param dst Reference to destination | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Dst_t> | |||||
idx_dev_t& operator>> (_Dst_t& dst) { | |||||
static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0), | |||||
"Target size must be a integer multiple of device's data size"); | |||||
get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t), cursor_()); | |||||
return *this; | |||||
} | |||||
//! Specialization to disallow pointer types as destination | |||||
template <typename _Dst_t> | |||||
idx_dev_t& operator>> (_Dst_t* dst) = delete; | |||||
//! Overload for single data_t object | |||||
idx_dev_t& operator>> (data_t& dst) { | |||||
get_ (dst, cursor_()); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator<< interface | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template operator<< implementation for for all by value/ref parameters | |||||
* \param src Reference to source data | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Src_t> | |||||
idx_dev_t& operator<< (_Src_t& src) { | |||||
static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0), | |||||
"Source size must be a integer multiple of device's data size"); | |||||
put_ (reinterpret_cast<data_t*>(&src), sizeof (_Src_t)/sizeof(data_t), cursor_()); | |||||
return *this; | |||||
} | |||||
//! specialization to disallow pointer types as source | |||||
template <typename _Src_t> | |||||
idx_dev_t& operator<< (_Src_t* src) = delete; | |||||
//! Overload for single data_t object | |||||
idx_dev_t& operator<< (const data_t& src) { | |||||
put_ (src, cursor_()); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name Helper operators | |||||
*/ | |||||
//!@{ | |||||
data_t& operator[] (const idx_t idx) { | |||||
iterator it(this, idx); | |||||
return *it; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Input iterator interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
using iterator = idxdev_it <idx_dev_t, data_t*, N>; //!< Iterator | |||||
using const_iterator = idxdev_it <idx_dev_t, const data_t*, N>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::beg); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/*! | |||||
* \brief | |||||
* A virtual base class specialization | |||||
* \param impl_t = virtual_tag | |||||
* \param data_t The devices base type of data | |||||
* \param idx_t The type to use for indexing | |||||
* \param streamsize The number of elements to indicate eos. | |||||
*/ | |||||
template <typename data_t, typename idx_t, size_t N> | |||||
class idx_dev <virtual_tag, data_t, idx_t, N> { | |||||
using idx_dev_t = idx_dev <virtual_tag, data_t, idx_t, N>; //!< class type syntactic sugar | |||||
//!@{ Export types as index device concept demands | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type= data_t*; | |||||
using idx_type = idx_t; | |||||
//!@} | |||||
using type = idx_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
virtual ~idx_dev () = default; //!< \brief Virtual destructor | |||||
protected: | |||||
idx_dev () = default; //!< \brief A default constructor from derived only | |||||
idx_dev(const idx_dev_t&) = delete; //!< No copies | |||||
idx_dev_t& operator= (const idx_dev_t&) = delete; //!< No copy assignments | |||||
//!@} | |||||
//!@{ \name Common index device interface requirements | |||||
private: | |||||
virtual size_t get_ (data_t&, idx_t) =0; | |||||
virtual size_t get_ (data_t*, size_t n, idx_t) =0; | |||||
virtual size_t put_ (const data_t&, idx_t) =0; | |||||
virtual size_t put_ (const data_t*, size_t n, idx_t) =0; | |||||
virtual idx_t cursor_ () const =0; | |||||
virtual void cursor_ (idx_t) =0; | |||||
//!@} | |||||
/*! | |||||
* \name Public index device interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
/*! | |||||
* \brief | |||||
* Get interface. This function should read | |||||
* a single data_t object from device in blocking mode. | |||||
* \param data Reference to data output from device. | |||||
* \return Number of data read from device | |||||
* \note | |||||
* A successful call should return 1 | |||||
*/ | |||||
size_t get (data_t& data, idx_t cursor) { | |||||
return get_ (data, cursor); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Old stile get functionality using free standing data_t*. | |||||
* This function should return a stream of data from device | |||||
* \param data Pointer to buffer to write the data from device. | |||||
* \param n The number of data of type data_t to read | |||||
* \return The read data items. | |||||
*/ | |||||
size_t get (data_t* data, size_t n, idx_t cursor) { | |||||
return get_ (data, n, cursor); | |||||
} | |||||
/*! | |||||
* \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, idx_t cursor) { | |||||
return put_ (data, cursor); | |||||
} | |||||
/*! | |||||
* \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, idx_t cursor) { | |||||
return put_ (data, n, cursor); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Return the current cursor position | |||||
*/ | |||||
idx_t cursor () const { return cursor_(); } | |||||
/*! | |||||
* \brief | |||||
* Set the cursor position | |||||
* \param idx Cursor address to set | |||||
* \return The current cursor | |||||
*/ | |||||
idx_t cursor (idx_t idx) { return cursor_(idx); } | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator>> interface | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template operator>> implementation for for all by value/ref parameters | |||||
* \param dst Reference to destination | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Dst_t> | |||||
idx_dev_t& operator>> (_Dst_t& dst) { | |||||
static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0), | |||||
"Target size must be an integer multiple of device's data size"); | |||||
get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t), cursor_()); | |||||
return *this; | |||||
} | |||||
//! specialization to disallow pointer types as destination | |||||
template <typename _Dst_t> | |||||
idx_dev_t& operator>> (_Dst_t* dst) = delete; | |||||
//! Overload for single data_t object | |||||
idx_dev_t& operator>> (data_t& dst) { | |||||
get_ (dst, cursor_()); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator<< interface | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template operator<< implementation for for all by value/ref parameters | |||||
* \param src Reference to source data | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Src_t> | |||||
idx_dev_t& operator<< (_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<data_t*>(&src), sizeof (_Src_t)/sizeof(data_t), cursor_()); | |||||
return *this; | |||||
} | |||||
//! specialization to disallow pointer types as source | |||||
template <typename _Src_t> | |||||
idx_dev_t& operator<< (_Src_t* src) = delete; | |||||
//! Overload for single data_t object | |||||
idx_dev_t& operator<< (const data_t& src) { | |||||
put_ (src, cursor_()); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name Helper operators | |||||
*/ | |||||
//!@{ | |||||
data_t& operator[] (const idx_t idx) { | |||||
iterator it(this, idx); | |||||
return *it; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Input iterator interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
using iterator = idxdev_it <idx_dev_t, data_t*, N>; //!< Iterator | |||||
using const_iterator = idxdev_it <idx_dev_t, const data_t*, N>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::beg); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/*! | |||||
* indexed device concept | |||||
*/ | |||||
//! @{ | |||||
#if defined _utl_have_concepts | |||||
template <typename _Tp> | |||||
concept bool Idx_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 | |||||
{t.get(v, 0)} -> size_t; | |||||
{t.get(&v, 1, 0)} -> size_t; | |||||
{t.put(v, 0)} -> size_t; | |||||
{t.put(&v, 1, 0)} -> size_t; | |||||
// Operators | |||||
t >> v; | |||||
t << v; | |||||
{t[typename _Tp::idx_type{}]} -> typename _Tp::data_type&; | |||||
// Iterators | |||||
requires idxdev_iterator_c<typename _Tp::iterator>; | |||||
typename _Tp::const_iterator; //XXX: change to concept: is_idxdev_iterator<_Tp> | |||||
//requires idxdev_iterator_c<typename _Tp::const_iterator>; | |||||
{ t.begin()} -> typename _Tp::iterator; | |||||
// {ct.begin()} -> 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; | |||||
}; | |||||
#else | |||||
namespace idx_dev_details { | |||||
using std::declval; | |||||
// main api members | |||||
template <class _Tp> using try_get1_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type&>())); | |||||
template <class _Tp> using try_get2_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type*>(), declval<size_t>())); | |||||
// operators | |||||
//template <class _Tp> using try_extract_t= decltype (declval<_Tp>() >> declval<typename _Tp::data_type&>()); | |||||
// iterator members | |||||
template <class _Tp> using try_begin_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_begin_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cbegin_t = decltype (declval<const _Tp>().cbegin()); | |||||
template <class _Tp> using try_end_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_end_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cend_t = decltype (declval<const _Tp>().cend()); | |||||
//! Primary template to catch any non input device types | |||||
template <typename _Tp, typename =void> | |||||
struct is_idx_dev_ : false_ {}; | |||||
//! template to catch a proper input device type | |||||
template <typename _Tp> | |||||
struct is_idx_dev_ <_Tp, | |||||
void_t < | |||||
typename _Tp::data_type, | |||||
typename _Tp::pointer_type, | |||||
typename _Tp::iterator, | |||||
typename _Tp::const_iterator, | |||||
use_if_same_t <try_get1_t <_Tp>, size_t>, | |||||
use_if_same_t <try_get2_t <_Tp>, size_t>, | |||||
//if_same_t <try_extract_t<_Tp>,typename _Tp&>, | |||||
use_if_same_t <try_begin_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_begin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cbegin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_end_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_end_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cend_t<_Tp>, typename _Tp::const_iterator> | |||||
> | |||||
> : true_ {}; | |||||
} | |||||
/*! | |||||
* Predicate for input device checking | |||||
* \param _Tp Type to check | |||||
* \return True if _Tp is a input device | |||||
*/ | |||||
template <typename _Tp> | |||||
constexpr bool Idx_dev = idx_dev_details::is_idx_dev_ <_Tp>::value; | |||||
#endif | |||||
//!@} | |||||
} | |||||
//!@} | |||||
#endif /* #ifndef __utl_dev_idx_dev_h__ */ |
@@ -0,0 +1,340 @@ | |||||
/*! | |||||
* \file utl/dev/in_dev.h | |||||
* \brief Abstract base class interface for input 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 <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_dev_in_dev_h__ | |||||
#define __utl_dev_in_dev_h__ | |||||
#include <utl/impl/impl.h> | |||||
#include <utl/helper/crtp.h> | |||||
#include <utl/meta/sfinae.h> | |||||
#include <utl/dev/dev_iterators.h> | |||||
namespace utl { | |||||
/*! | |||||
* \ingroup Device Interface | |||||
* \brief Abstract base class for input devices | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template base class for un-buffered input devices. using CRTP | |||||
* | |||||
* This class force a common interface for input, non-buffered devices. | |||||
* By using this common interface the class implements | |||||
* - Stream-like extracting operator | |||||
* - Input iterator | |||||
* - Const input 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 | |||||
* \param streamsize The number of elements to indicate eos. | |||||
* \arg None or 0 Stream only. No iterator as begin() now equals end(). | |||||
*/ | |||||
template <typename impl_t, typename data_t, size_t streamsize =0> | |||||
class in_dev { | |||||
_CRTP_IMPL(impl_t); | |||||
using in_dev_t = in_dev <impl_t, data_t, streamsize>; //!< class type syntactic sugar | |||||
//! Export types as input device concept demands | |||||
//! @{ | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type = data_t*; | |||||
//! @} | |||||
using type = in_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
protected: | |||||
~in_dev () = default; //!< \brief Allow destructor from derived only | |||||
in_dev () = default; //!< \brief A default constructor from derived only | |||||
in_dev(const in_dev_t&) = delete; //!< No copies | |||||
in_dev_t& operator= (const in_dev_t&) = delete; //!< No copy assignments | |||||
//!@} | |||||
//! \name Common input device interface requirements | |||||
//! @{ | |||||
private: | |||||
size_t get_ (data_t& data) { return impl().get_ (data); } | |||||
size_t get_ (data_t* data, size_t n) { return impl().get (data, n); } | |||||
//! @} | |||||
/*! | |||||
* \name Public Get interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
/*! | |||||
* \brief | |||||
* Get interface. This function should read | |||||
* a single data_t object from device in blocking mode. | |||||
* \param data Reference to data output from device. | |||||
* \return Number of data read from device | |||||
* \note | |||||
* A successful call should return 1 | |||||
*/ | |||||
size_t get (data_t& data) { | |||||
return get_ (data); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Old stile get functionality using free standing data_t*. | |||||
* This function should return a stream of data from device | |||||
* \param data Pointer to buffer to write the data from device. | |||||
* \param n The number of data of type data_t to read | |||||
* \return The read data items. | |||||
*/ | |||||
size_t get (data_t* data, size_t n) { | |||||
return get_ (data, n); | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator >> interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
/*! | |||||
* \brief | |||||
* Template operator >> implementation for for all by value/ref parameters | |||||
* \note | |||||
* In the case _Dst_t size is not a integer multiple of device's data size | |||||
* this will fail by static assertion | |||||
* \param dst Reference to destination | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Dst_t> | |||||
in_dev_t& operator>> (_Dst_t& dst) { | |||||
static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0), | |||||
"Target size must be an integer multiple of device's data size"); | |||||
get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t)); | |||||
return *this; | |||||
} | |||||
//! Specialization to disallow pointer types as destination | |||||
template <typename _Dst_t> | |||||
in_dev_t& operator>> (_Dst_t* dst) = delete; | |||||
//! Overload for single data_t object | |||||
in_dev_t& operator>> (data_t& dst) { | |||||
get_ (dst); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Input iterator interface | |||||
*/ | |||||
//!@{ | |||||
using iterator = indev_it <in_dev_t, data_t*, streamsize>; //!< Iterator | |||||
using const_iterator = indev_it <in_dev_t, const data_t*, streamsize>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::init); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::init); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::init); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/*! | |||||
* \brief | |||||
* A virtual base class specialization | |||||
* \param impl_t = virtual_tag | |||||
* \param data_t The devices base type of data | |||||
* \param streamsize_t Type to hold the number of read bytes | |||||
*/ | |||||
template <typename data_t, size_t streamsize> | |||||
class in_dev <virtual_tag, data_t, streamsize> { | |||||
//!< class type syntactic sugar | |||||
using in_dev_t = in_dev <virtual_tag, data_t, streamsize>; | |||||
//! Export types as input device concept demands | |||||
//!@{ | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type = data_t*; | |||||
//!@} | |||||
using type = in_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
virtual ~in_dev () = default; //!< \brief Virtual destructor | |||||
protected: | |||||
in_dev () = default; //!< \brief A default constructor from derived only | |||||
in_dev(const in_dev_t&) = delete; //!< No copies | |||||
in_dev_t& operator= (const in_dev_t&) = delete; //!< No copy assignments | |||||
//!@} | |||||
/*! | |||||
* \name Common input device interface requirements | |||||
*/ | |||||
//!@{ | |||||
private: | |||||
/*! | |||||
* \brief | |||||
* Get interface. This function should read | |||||
* a single data_t object from device in blocking mode. | |||||
* \param data Reference to data output from device. | |||||
* \return Number of data read from device | |||||
* \note | |||||
* A successful call should return 1 | |||||
*/ | |||||
virtual size_t get_ (data_t& data) = 0; | |||||
/*! | |||||
* \brief | |||||
* Old stile get functionality using free standing data_t*. | |||||
* This function should return a stream of data from device | |||||
* \param data Pointer to buffer to write the data from device. | |||||
* \param n The number of data of type data_t to read | |||||
* \return The read data items. | |||||
*/ | |||||
virtual size_t get_ (data_t* data, size_t n) = 0; | |||||
//!@} | |||||
/*! | |||||
* \name Public Get interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
size_t get (data_t& data) { return get_ (data); } | |||||
size_t get (data_t* data, size_t n) { return get_ (data, n); } | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator >> interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
/*! | |||||
* \brief | |||||
* Template operator >> implementation for for all by value/ref parameters | |||||
* \note | |||||
* In the case _Dst_t size is not a integer multiple of device's data size | |||||
* this will fail by static assertion | |||||
* \param dst Reference to destination | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Dst_t> | |||||
in_dev_t& operator>> (_Dst_t& dst) { | |||||
static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0), | |||||
"Target size must be an integer multiple of device's data size"); | |||||
get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t)); | |||||
return *this; | |||||
} | |||||
//! specialization to disallow pointer types as destination | |||||
template <typename _Dst_t> | |||||
in_dev_t& operator>> (_Dst_t* dst) = delete; | |||||
//! Overload for single data_t object | |||||
in_dev_t& operator>> (data_t& dst) { | |||||
get_ (dst); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Input iterator interface | |||||
*/ | |||||
//!@{ | |||||
using iterator = indev_it <in_dev_t, data_t*, streamsize>; //!< Iterator | |||||
using const_iterator = indev_it <in_dev_t, const data_t*, streamsize>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::init); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::init); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::init); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/* | |||||
* Input device predicate (concept) | |||||
*/ | |||||
namespace in_dev_details { | |||||
using std::declval; | |||||
// main api members | |||||
template <class _Tp> using try_get1_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type&>())); | |||||
template <class _Tp> using try_get2_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type*>(), declval<size_t>())); | |||||
// operators | |||||
//template <class _Tp> using try_extract_t= decltype (declval<_Tp>() >> declval<typename _Tp::data_type&>()); | |||||
// iterator members | |||||
template <class _Tp> using try_begin_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_begin_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cbegin_t = decltype (declval<const _Tp>().cbegin()); | |||||
template <class _Tp> using try_end_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_end_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cend_t = decltype (declval<const _Tp>().cend()); | |||||
//! Primary template to catch any non input device types | |||||
template <typename _Tp, typename =void> | |||||
struct is_in_dev_ : false_ {}; | |||||
//! template to catch a proper input device type | |||||
template <typename _Tp> | |||||
struct is_in_dev_ <_Tp, | |||||
void_t < | |||||
typename _Tp::data_type, | |||||
typename _Tp::pointer_type, | |||||
typename _Tp::iterator, | |||||
typename _Tp::const_iterator, | |||||
use_if_same_t <try_get1_t <_Tp>, size_t>, | |||||
use_if_same_t <try_get2_t <_Tp>, size_t>, | |||||
//if_same_t <try_extract_t<_Tp>,typename _Tp&>, | |||||
use_if_same_t <try_begin_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_begin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cbegin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_end_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_end_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cend_t<_Tp>, typename _Tp::const_iterator> | |||||
> | |||||
> : true_ {}; | |||||
} | |||||
/*! | |||||
* Predicate for input device checking | |||||
* \param _Tp Type to check | |||||
* \return True if _Tp is a input device | |||||
*/ | |||||
template <typename _Tp> | |||||
constexpr bool In_dev = in_dev_details::is_in_dev_ <_Tp>::value; | |||||
} | |||||
//!@} | |||||
#endif /* #ifndef __utl_dev_in_dev_h__ */ |
@@ -0,0 +1,386 @@ | |||||
/*! | |||||
* \file utl/dev/inbuf_dev.h | |||||
* \brief Abstract base class interface for input buffered | |||||
* 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 <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_dev_inbuf_dev_h__ | |||||
#define __utl_dev_inbuf_dev_h__ | |||||
#include <utl/impl/impl.h> | |||||
#include <utl/helper/crtp.h> | |||||
#include <utl/meta/sfinae.h> | |||||
#include <utl/dev/dev_iterators.h> | |||||
namespace utl { | |||||
/*! | |||||
* \ingroup Device Interface | |||||
* \brief Abstract base class for input buffered devices | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template base class for buffered input buffered devices. using CRTP | |||||
* | |||||
* This class force a common interface for input, buffered devices. | |||||
* By using this common interface the class implements | |||||
* - Stream-like extracting operator | |||||
* - Input iterator | |||||
* - Const input 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 | |||||
* \param streamsize The number of elements to indicate eos. | |||||
* \arg None or 0 Stream only. No iterator as begin() now equals end(). | |||||
*/ | |||||
template <typename impl_t, typename data_t, size_t streamsize =0> | |||||
class inbuf_dev { | |||||
_CRTP_IMPL(impl_t); | |||||
using inbuf_dev_t = inbuf_dev <impl_t, data_t, streamsize>; //!< class type syntactic sugar | |||||
//! Export types as input device concept demands | |||||
//!@{ | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type = data_t*; | |||||
//!@} | |||||
using type = inbuf_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
protected: | |||||
~inbuf_dev () = default; //!< \brief Allow destructor from derived only | |||||
inbuf_dev () = default; //!< \brief A default constructor from derived only | |||||
inbuf_dev(const inbuf_dev_t&) = delete; //!< No copies | |||||
inbuf_dev_t& operator= (const inbuf_dev_t&) = delete; //!< No copy assignments | |||||
//!@} | |||||
//! \name Common input device interface requirements | |||||
//!@{ | |||||
private: | |||||
size_t in_avail_ () { return impl().in_avail_(); } | |||||
size_t get_ (data_t& data) { return impl().get_(data); } | |||||
size_t get_ (data_t* data, size_t n) { return impl().get_ (data, n); } | |||||
//!@} | |||||
/*! | |||||
* \name Public Get interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
/*! | |||||
* \return | |||||
* The available @a data_t typed items in input buffer. | |||||
*/ | |||||
size_t in_avail () { | |||||
return in_avail_ (); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* The base get interface. This function should read | |||||
* a single data_t object from device usually in non-blocking mode. | |||||
* \param data Reference to data output from device. | |||||
* \return Number of data read from device | |||||
* \note | |||||
* A successful call should return 1 | |||||
*/ | |||||
size_t get (data_t& data) { | |||||
return get_ (data); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Old stile get functionality using free standing data_t*. | |||||
* This function should return a stream of data from device | |||||
* \param data Pointer to buffer to write the data from device. | |||||
* \param n The number of data of type data_t to read | |||||
* \return The read data items. | |||||
*/ | |||||
size_t get (data_t* data, size_t n) { | |||||
return get_ (data, n); | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator >> interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
/*! | |||||
* \brief | |||||
* Template operator >> implementation for for all by value/ref parameters | |||||
* \note | |||||
* In the case _Dst_t size is not a integer multiple of device's data size | |||||
* this will fail by static assertion | |||||
* \param dst Reference to destination | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Dst_t> | |||||
inbuf_dev_t& operator>> (_Dst_t& dst) { | |||||
static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0), | |||||
"Target size must be an integer multiple of device's data size"); | |||||
get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t)); | |||||
return *this; | |||||
} | |||||
//! Specialization to disallow pointer types as destination | |||||
template <typename _Dst_t> | |||||
inbuf_dev_t& operator>> (_Dst_t* dst) = delete; | |||||
//! Overload for single data_t object | |||||
inbuf_dev_t& operator>> (data_t& dst) { | |||||
get_ (dst); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Input iterator interface | |||||
*/ | |||||
//!@{ | |||||
using iterator = indev_it <inbuf_dev_t, data_t*, streamsize>; //!< Iterator | |||||
using const_iterator = indev_it <inbuf_dev_t, const data_t*, streamsize>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::init); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::init); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::init); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/*! | |||||
* \brief | |||||
* A virtual base class specialization | |||||
* \param impl_t = virtual_tag | |||||
* \param data_t The devices base type of data | |||||
* \param streamsize The number of elements to indicate eos. | |||||
* \arg None or 0 Stream only. No iterator as begin() now equals end(). | |||||
*/ | |||||
template <typename data_t, size_t streamsize> | |||||
class inbuf_dev <virtual_tag, data_t, streamsize> { | |||||
//!< class type syntactic sugar | |||||
using inbuf_dev_t = inbuf_dev <virtual_tag, data_t, streamsize>; | |||||
//! Export types as input device concept demands | |||||
//!@{ | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type = data_t*; | |||||
//!@} | |||||
using type = inbuf_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
virtual ~inbuf_dev () = default; //!< \brief Virtual destructor | |||||
protected: | |||||
inbuf_dev () = default; //!< \brief A default constructor from derived only | |||||
inbuf_dev(const inbuf_dev_t&) = delete; //!< No copies | |||||
inbuf_dev_t& operator= (const inbuf_dev_t&) = delete; //!< No copy assignments | |||||
//!@} | |||||
/*! | |||||
* \name Common input device interface requirements | |||||
*/ | |||||
//!@{ | |||||
private: | |||||
/*! | |||||
* \return | |||||
* The available @a data_t typed items in input buffer. | |||||
*/ | |||||
virtual size_t in_avail_ () = 0; | |||||
/*! | |||||
* \brief | |||||
* The base get interface. This function should read | |||||
* a single data_t object from device usually in non-blocking mode. | |||||
* \param data Reference to data output from device. | |||||
* \return Number of data read from device | |||||
* \note | |||||
* A successful call should return 1 | |||||
*/ | |||||
virtual size_t get_ (data_t& data) = 0; | |||||
/*! | |||||
* \brief | |||||
* Old stile get functionality using free standing data_t*. | |||||
* This function should return a stream of data from device | |||||
* \param data Pointer to buffer to write the data from device. | |||||
* \param n The number of data of type data_t to read | |||||
* \return The read data items. | |||||
*/ | |||||
virtual size_t get_ (data_t* data, size_t n) = 0; | |||||
//!@} | |||||
/*! | |||||
* \name Public Get interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
size_t in_avail() { return in_avail_(); } | |||||
size_t get (data_t& data) { return get_ (data); } | |||||
size_t get (data_t* data, size_t n) { return get_ (data, n); } | |||||
//!@} | |||||
/*! | |||||
* \name Stream operator >> interface | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template operator >> implementation for for all by value/ref parameters | |||||
* \note | |||||
* In the case _Dst_t size is not a integer multiple of device's data size | |||||
* this will fail by static assertion | |||||
* \param dst Reference to destination | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Dst_t> | |||||
inbuf_dev_t& operator>> (_Dst_t& dst) { | |||||
static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0), | |||||
"Target size must be an integer multiple of device's data size"); | |||||
get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t)); | |||||
return *this; | |||||
} | |||||
//! specialization to disallow pointer types as destination | |||||
template <typename _Dst_t> | |||||
inbuf_dev_t& operator>> (_Dst_t* dst) = delete; | |||||
//! Overload for single data_t object | |||||
inbuf_dev_t& operator>> (data_t& dst) { | |||||
get_ (dst); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Input iterator interface | |||||
*/ | |||||
//!@{ | |||||
using iterator = indev_it <inbuf_dev_t, data_t*, streamsize>; //!< Iterator | |||||
using const_iterator = indev_it <inbuf_dev_t, const data_t*, streamsize>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::init); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::init); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::init); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/*! | |||||
* Input buffer device concept | |||||
*/ | |||||
//! @{ | |||||
#if defined _utl_have_concepts | |||||
template <typename _Tp> | |||||
concept bool Inbuf_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 | |||||
{t.get(v, 0)} -> size_t; | |||||
{t.get(&v, 1, 0)} -> size_t; | |||||
// Operators | |||||
t >> v; | |||||
// Iterators | |||||
typename _Tp::const_iterator; //XXX: change to concept: is_idxdev_iterator<_Tp> | |||||
requires Indev_it<typename _Tp::iterator>; | |||||
//requires Indev_it<typename _Tp::const_iterator>; | |||||
{ t.begin() } -> typename _Tp::iterator; | |||||
// {ct.begin()} -> 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; | |||||
}; | |||||
#else | |||||
namespace inbuf_dev_details { | |||||
using std::declval; | |||||
// main api members | |||||
template <class _Tp> using try_get1_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type&>())); | |||||
template <class _Tp> using try_get2_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type*>(), declval<size_t>())); | |||||
// operators | |||||
//template <class _Tp> using try_extract_t= decltype (declval<_Tp>() >> declval<typename _Tp::data_type&>()); | |||||
// iterator members | |||||
template <class _Tp> using try_begin_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_begin_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cbegin_t = decltype (declval<const _Tp>().cbegin()); | |||||
template <class _Tp> using try_end_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_end_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cend_t = decltype (declval<const _Tp>().cend()); | |||||
//! Primary template to catch any non input device types | |||||
template <typename _Tp, typename =void> | |||||
struct is_inbuf_dev_ : false_ {}; | |||||
//! template to catch a proper input device type | |||||
template <typename _Tp> | |||||
struct is_inbuf_dev_ <_Tp, | |||||
void_t < | |||||
typename _Tp::data_type, | |||||
typename _Tp::pointer_type, | |||||
typename _Tp::iterator, | |||||
typename _Tp::const_iterator, | |||||
use_if_same_t <try_get1_t <_Tp>, size_t>, | |||||
use_if_same_t <try_get2_t <_Tp>, size_t>, | |||||
//if_same_t <try_extract_t<_Tp>,typename _Tp&>, | |||||
use_if_same_t <try_begin_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_begin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cbegin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_end_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_end_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cend_t<_Tp>, typename _Tp::const_iterator> | |||||
> | |||||
> : true_ {}; | |||||
} | |||||
/*! | |||||
* Predicate for input device checking | |||||
* \param _Tp Type to check | |||||
* \return True if _Tp is a input device | |||||
*/ | |||||
template <typename _Tp> | |||||
constexpr bool Inbuf_dev = inbuf_dev_details::is_inbuf_dev_ <_Tp>::value; | |||||
#endif | |||||
//!@} | |||||
} | |||||
//!@} | |||||
#endif /* #ifndef __utl_dev_inbuf_dev_h__ */ |
@@ -0,0 +1,365 @@ | |||||
/*! | |||||
* \file utl/dev/out_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 <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#ifndef __utl_dev_out_dev_h__ | |||||
#define __utl_dev_out_dev_h__ | |||||
#include <utl/impl/impl.h> | |||||
#include <utl/helper/crtp.h> | |||||
#include <utl/meta/sfinae.h> | |||||
#include <utl/dev/dev_iterators.h> | |||||
namespace utl { | |||||
/*! | |||||
* \ingroup Device Interface | |||||
* \brief Abstract base class for output devices | |||||
*/ | |||||
//!@{ | |||||
/*! | |||||
* \brief | |||||
* Template base class for output devices. using CRTP | |||||
* | |||||
* This class force a common interface for output 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 | |||||
* \param streamsize The number of elements to indicate eos. | |||||
* \arg None or 0 Stream only. No iterator as begin() now equals end(). | |||||
*/ | |||||
template <typename impl_t, typename data_t, size_t streamsize =0> | |||||
class out_dev { | |||||
_CRTP_IMPL(impl_t); | |||||
using out_dev_t = out_dev <impl_t, data_t, streamsize>; //!< class type syntactic sugar | |||||
//! Export types as input device concept demands | |||||
//! @{ | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type = data_t*; | |||||
//!@} | |||||
using type = out_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
protected: | |||||
~out_dev () = default; //!< \brief Allow destructor from derived only | |||||
out_dev () = default; //!< \brief A default constructor from derived only | |||||
out_dev(const out_dev_t&) = delete; //!< No copies | |||||
out_dev_t& operator= (const out_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 <typename _Src_t> | |||||
out_dev_t& operator<< (_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<data_t*>(&src), sizeof(_Src_t)/sizeof(data_t)); | |||||
return *this; | |||||
} | |||||
//! specialization to disallow pointer types as source | |||||
template <typename _Src_t> | |||||
out_dev_t& operator<< (_Src_t* src) = delete; | |||||
//! Overload for single data_t object | |||||
out_dev_t& operator<< (const data_t& src) { | |||||
put_ (src); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Output iterator interface | |||||
*/ | |||||
//!@{ | |||||
using iterator = outdev_it <out_dev_t, data_t*, streamsize>; //!< Iterator | |||||
using const_iterator = outdev_it <out_dev_t, const data_t*, streamsize>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::beg); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/*! | |||||
* \brief | |||||
* A virtual base class specialization | |||||
* \param impl_t = virtual_tag | |||||
* \param data_t The devices base type of data | |||||
* \param streamsize The number of elements to indicate eos. | |||||
* \arg None or 0 Stream only. No iterator as begin() now equals end(). | |||||
*/ | |||||
template <typename data_t, size_t streamsize> | |||||
class out_dev <virtual_tag, data_t, streamsize> { | |||||
using out_dev_t = out_dev <virtual_tag, data_t, streamsize>; //!< class type syntactic sugar | |||||
//! Export types as input device concept demands | |||||
//! @{ | |||||
public: | |||||
using data_type = data_t; | |||||
using pointer_type = data_t*; | |||||
//! @} | |||||
using type = out_dev_t; //!< Export type as identity meta-function | |||||
/*! | |||||
* \name Constructor / Destructor | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
virtual ~out_dev () = default; //!< \brief Virtual destructor | |||||
protected: | |||||
out_dev () = default; //!< \brief A default constructor from derived only | |||||
out_dev(const out_dev&) = delete; //!< No copies | |||||
out_dev_t& operator= (const out_dev_t&) = delete; //!< No copy assignments | |||||
//!@} | |||||
/*! | |||||
* \name Common output device interface requirements | |||||
*/ | |||||
//!@{ | |||||
private: | |||||
/*! | |||||
* \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 | |||||
*/ | |||||
virtual size_t put_ (const data_t& data) = 0; | |||||
/*! | |||||
* \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. | |||||
*/ | |||||
virtual size_t put_ (const data_t* data, size_t n) = 0; | |||||
//!@} | |||||
/*! | |||||
* \name Public Put interface | |||||
*/ | |||||
//!@{ | |||||
public: | |||||
size_t put (const data_t& data) { return put_ (data); } | |||||
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 | |||||
* \param src Reference to source data | |||||
* \return Reference to this device for chaining | |||||
*/ | |||||
template <typename _Src_t> | |||||
out_dev_t& operator<< (_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<data_t*>(&src), sizeof(_Src_t)/sizeof(data_t)); | |||||
return *this; | |||||
} | |||||
//! specialization to disallow pointer types as source | |||||
template <typename _Src_t> | |||||
out_dev_t& operator<< (_Src_t* src) = delete; | |||||
//! Overload for single data_t object | |||||
out_dev_t& operator<< (const data_t& src) { | |||||
put_ (src); | |||||
return *this; | |||||
} | |||||
//!@} | |||||
/*! | |||||
* \name STL-like Output iterator interface | |||||
*/ | |||||
//!@{ | |||||
using iterator = outdev_it <out_dev_t, data_t*, streamsize>; //!< Iterator | |||||
using const_iterator = outdev_it <out_dev_t, const data_t*, streamsize>; //!< Const iterator | |||||
//!@{ .begin implementation | |||||
iterator begin () noexcept { return iterator(this, iterator::beg); } | |||||
const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); } | |||||
//!@} | |||||
//!@{ .end implementation | |||||
iterator end () noexcept { return iterator(this, iterator::eos); } | |||||
const_iterator end () const noexcept { return const_iterator(this, iterator::eos); } | |||||
const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); } | |||||
//!@} | |||||
//!@} | |||||
}; | |||||
/*! | |||||
* Output device concept | |||||
*/ | |||||
//! @{ | |||||
#if defined _utl_have_concepts | |||||
template <typename _Tp> | |||||
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 | |||||
{t.put(v)} -> size_t; | |||||
{t.put(&v, sizeof(v))} -> size_t; | |||||
// Operators | |||||
t << v; | |||||
// Iterators | |||||
typename _Tp::const_iterator; //XXX: change to concept: is_idxdev_iterator<_Tp> | |||||
requires Outdev_it<typename _Tp::iterator>; | |||||
// requires Outdev_it<typename _Tp::const_iterator>; | |||||
{ t.begin() } -> typename _Tp::iterator; | |||||
{ct.begin()} -> 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; | |||||
}; | |||||
#else | |||||
namespace out_dev_details { | |||||
using std::declval; | |||||
template <class _Tp> using try_put1_t = decltype (declval<_Tp>().put (declval<const typename _Tp::data_type&>())); | |||||
template <class _Tp> using try_put2_t = decltype (declval<_Tp>().put (declval<const typename _Tp::data_type*>(), | |||||
declval<size_t>())); | |||||
// operators | |||||
//template <class _Tp> using try_insert_t= decltype (declval<_Tp>() << declval<typename _Tp::data_type&>()); | |||||
// iterator members | |||||
template <class _Tp> using try_begin_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_begin_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cbegin_t = decltype (declval<const _Tp>().cbegin()); | |||||
template <class _Tp> using try_end_t = decltype (declval<_Tp>().begin()); | |||||
template <class _Tp> using tryc_end_t = decltype (declval<const _Tp>().begin()); | |||||
template <class _Tp> using try_cend_t = decltype (declval<const _Tp>().cend()); | |||||
//! Primary template to catch any non output device types | |||||
template <typename _Tp, typename =void> | |||||
struct is_out_dev_ : false_ { }; | |||||
//! template to catch a proper output device type | |||||
template <typename _Tp> | |||||
struct is_out_dev_ <_Tp, | |||||
void_t < | |||||
typename _Tp::data_type, | |||||
typename _Tp::pointer_type, | |||||
typename _Tp::iterator, | |||||
typename _Tp::const_iterator, | |||||
use_if_same_t <try_put1_t <_Tp>, size_t>, | |||||
use_if_same_t <try_put2_t <_Tp>, size_t>, | |||||
//if_same_t <try_insert_t<_Tp>,_Tp&>, | |||||
use_if_same_t <try_begin_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_begin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cbegin_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_end_t<_Tp>, typename _Tp::iterator>, | |||||
use_if_same_t <tryc_end_t<_Tp>, typename _Tp::const_iterator>, | |||||
use_if_same_t <try_cend_t<_Tp>, typename _Tp::const_iterator> | |||||
> | |||||
> : true_ { }; | |||||
} | |||||
/*! | |||||
* Predicate for output device checking | |||||
* \param _Tp Type to check | |||||
* \return True if _Tp is a output device | |||||
*/ | |||||
template <typename _Tp> | |||||
constexpr bool Out_dev = out_dev_details::is_out_dev_<_Tp>::value; | |||||
#endif | |||||
//!@} | |||||
//!@} | |||||
} //namespace utl | |||||
#endif /* #ifndef __utl_dev_out_dev_h__ */ |