/*!
* \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 .
*
*/
#ifndef __utl_dev_idx_dev_h__
#define __utl_dev_idx_dev_h__
#include
#include
#include
#include
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
class idx_dev {
_CRTP_IMPL(impl_t);
using idx_dev_t = idx_dev ; //!< 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
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(&dst), sizeof(_Dst_t)/sizeof(data_t), cursor_());
return *this;
}
//! Specialization to disallow pointer types as destination
template
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
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(&src), sizeof (_Src_t)/sizeof(data_t), cursor_());
return *this;
}
//! specialization to disallow pointer types as source
template
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 ; //!< Iterator
using const_iterator = idxdev_it ; //!< 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
class idx_dev {
using idx_dev_t = idx_dev ; //!< 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
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(&dst), sizeof(_Dst_t)/sizeof(data_t), cursor_());
return *this;
}
//! specialization to disallow pointer types as destination
template
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
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(&src), sizeof (_Src_t)/sizeof(data_t), cursor_());
return *this;
}
//! specialization to disallow pointer types as source
template
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 ; //!< Iterator
using const_iterator = idxdev_it ; //!< 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
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::const_iterator; //XXX: change to concept: is_idxdev_iterator<_Tp>
//requires idxdev_iterator_c;
{ 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 using try_get1_t = decltype (declval<_Tp>().get (declval()));
template using try_get2_t = decltype (declval<_Tp>().get (declval(), declval()));
// operators
//template using try_extract_t= decltype (declval<_Tp>() >> declval());
// iterator members
template using try_begin_t = decltype (declval<_Tp>().begin());
template using tryc_begin_t = decltype (declval().begin());
template using try_cbegin_t = decltype (declval().cbegin());
template using try_end_t = decltype (declval<_Tp>().begin());
template using tryc_end_t = decltype (declval().begin());
template using try_cend_t = decltype (declval().cend());
//! Primary template to catch any non input device types
template
struct is_idx_dev_ : false_ {};
//! template to catch a proper input device type
template
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 , size_t>,
use_if_same_t , size_t>,
//if_same_t ,typename _Tp&>,
use_if_same_t , typename _Tp::iterator>,
use_if_same_t , typename _Tp::const_iterator>,
use_if_same_t , typename _Tp::const_iterator>,
use_if_same_t , typename _Tp::iterator>,
use_if_same_t , typename _Tp::const_iterator>,
use_if_same_t , typename _Tp::const_iterator>
>
> : true_ {};
}
/*!
* Predicate for input device checking
* \param _Tp Type to check
* \return True if _Tp is a input device
*/
template
constexpr bool Idx_dev = idx_dev_details::is_idx_dev_ <_Tp>::value;
#endif
//!@}
}
//!@}
#endif /* #ifndef __utl_dev_idx_dev_h__ */