|
- /*!
- * \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/core/impl.h>
- #include <utl/core/crtp.h>
- #include <utl/dev/dev_iterators.h>
- #include <utl/meta/meta.h>
-
- namespace utl {
-
- /*!
- * \ingroup Device Interface
- * \brief Abstract base classes 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;
- }
- //! Overload 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 <const out_dev_t, 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;
- }
- //! Overload 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 <const out_dev_t, 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_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;
- 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__ */
|