/*! * \file utl/dev/ostream_dev.h * \brief Abstract base class interface for output devices of utl. * * Copyright (C) 2018 Christos Choutouridis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef __utl_dev_ostream_dev_h__ #define __utl_dev_ostream_dev_h__ #include #include #include #include namespace utl { /*! * \ingroup Device Interface * \brief Abstract base classes for output stream devices */ //!@{ /*! * \brief * Template base class for output stream devices using CRTP * * This class force a common interface for output stream devices. * By using this common interface the class implements * - Stream-like inserting operator * - Output iterator * - Const output iterator * to inherit to implementation. * * \param impl_t The CRTP type (the derived/implementation class typename). * \param data_t The devices base type of data */ template class ostream_dev { _CRTP_IMPL(impl_t); using ostream_dev_t = ostream_dev ; //!< class type syntactic sugar //! Export types as output device concept demands //! @{ public: using data_type = data_t; using pointer_type = data_t*; //!@} using type = ostream_dev_t; //!< Export type as identity meta-function /*! * \name Constructor / Destructor */ //!@{ protected: ~ostream_dev () = default; //!< \brief Allow destructor from derived only ostream_dev () = default; //!< \brief A default constructor from derived only ostream_dev(const ostream_dev_t&) = delete; //!< No copies ostream_dev_t& operator= (const ostream_dev_t&) = delete; //!< No copy assignments //!@} //! \name Common output device interface requirements //!@{ private: size_t put_ (const data_t& data) { return impl().put_ (data); } size_t put_ (const data_t* data, size_t n) { return impl().put_ (data, n); } //!@} /*! * \name Common output device interface */ //!@{ public: /*! * \brief * Put interface. This function should send a single * data_t object to device. * \param data The data to send * \return The number of transmitted data items * \note * A successful call should return 1 */ size_t put (const data_t& data) { return put_ (data); } /*! * \brief * Put interface. This function should send a stream of * data_t objects to device. * \param data Pointer to buffer indenting write to device. * \param n The number of data of type data_t to send * \return The number of transmitted items. */ size_t put (const data_t* data, size_t n) { return put_ (data, n); } //!@} /*! * \name Stream operator << interface */ //!@{ public: /*! * \brief * Template operator<< implementation for for all by value/ref parameters * \note * In the case _Src_t size is not an exact multiple of data_t size * the write data will be truncated and there may be data loss. * \param src Reference to source data * \return Reference to this device for chaining */ template ostream_dev_t& operator<< (const _Src_t& src) { static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0), "Source size must be an integer multiple of device's data size"); put_ (reinterpret_cast(&src), sizeof(_Src_t)/sizeof(data_t)); return *this; } //! Overload to disallow pointer types as source template ostream_dev_t& operator<< (_Src_t* src) = delete; //! Overload for single data_t object ostream_dev_t& operator<< (const data_t& src) { put_ (src); return *this; } //ToDo: Add support for c-string, utl::string, ... //!@} /*! * \name STL-like Output iterator interface */ //!@{ using iterator = ostreamdev_it ; //!< Iterator using const_iterator = ostreamdev_it ; //!< Const iterator //!@{ .begin implementation iterator begin () noexcept { return iterator(this); } const_iterator begin () const noexcept { return const_iterator(this); } const_iterator cbegin () const noexcept { return const_iterator(this); } //!@} //!@{ .end implementation iterator end () noexcept { return iterator(); } const_iterator end () const noexcept { return const_iterator(); } const_iterator cend () const noexcept { return const_iterator(); } //!@} //!@} }; template class ostream_dev { using ostream_dev_t = ostream_dev ; //!< class type syntactic sugar //! Export types as output device concept demands //! @{ public: using data_type = data_t; using pointer_type = data_t*; //!@} using type = ostream_dev_t; //!< Export type as identity meta-function /*! * \name Constructor / Destructor */ //!@{ public: virtual ~ostream_dev () = default; //!< \brief Virtual destructor protected: ostream_dev () = default; //!< \brief A default constructor from derived only ostream_dev(const ostream_dev_t&) = delete; //!< No copies ostream_dev_t& operator= (const ostream_dev_t&) = delete; //!< No copy assignments //!@} //! \name Common output device interface requirements //!@{ private: virtual size_t put_ (const data_t& data) =0; virtual size_t put_ (const data_t* data, size_t n) =0; //!@} /*! * \name Common output device interface */ //!@{ public: /*! * \brief * Put interface. This function should send a single * data_t object to device. * \param data The data to send * \return The number of transmitted data items * \note * A successful call should return 1 */ size_t put (const data_t& data) { return put_ (data); } /*! * \brief * Put interface. This function should send a stream of * data_t objects to device. * \param data Pointer to buffer indenting write to device. * \param n The number of data of type data_t to send * \return The number of transmitted items. */ size_t put (const data_t* data, size_t n) { return put_ (data, n); } //!@} /*! * \name Stream operator << interface */ //!@{ public: /*! * \brief * Template operator<< implementation for for all by value/ref parameters * \note * In the case _Src_t size is not an exact multiple of data_t size * the write data will be truncated and there may be data loss. * \param src Reference to source data * \return Reference to this device for chaining */ template ostream_dev_t& operator<< (const _Src_t& src) { static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0), "Source size must be an integer multiple of device's data size"); put_ (reinterpret_cast(&src), sizeof(_Src_t)/sizeof(data_t)); return *this; } //! Overload to disallow pointer types as source template ostream_dev_t& operator<< (_Src_t* src) = delete; //! Overload for single data_t object ostream_dev_t& operator<< (const data_t& src) { put_ (src); return *this; } //!@} /*! * \name STL-like Output iterator interface */ //!@{ using iterator = ostreamdev_it ; //!< Iterator using const_iterator = ostreamdev_it ; //!< Const iterator //!@{ .begin implementation iterator begin () noexcept { return iterator(this); } const_iterator begin () const noexcept { return const_iterator(this); } const_iterator cbegin () const noexcept { return const_iterator(this); } //!@} //!@{ .end implementation iterator end () noexcept { return iterator(); } const_iterator end () const noexcept { return const_iterator(); } const_iterator cend () const noexcept { return const_iterator(); } //!@} //!@} }; //!@} } //namespace utl #endif /* #ifndef __utl_dev_out_dev_h__ */