387 lines
14 KiB
C++
387 lines
14 KiB
C++
/*!
|
|
* \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__ */
|