Micro template library A library for building device drivers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

365 lines
14 KiB

  1. /*!
  2. * \file utl/dev/out_dev.h
  3. * \brief Abstract base class interface for output devices of utl.
  4. *
  5. * Copyright (C) 2018 Christos Choutouridis
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation, either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. #ifndef __utl_dev_out_dev_h__
  22. #define __utl_dev_out_dev_h__
  23. #include <utl/core/impl.h>
  24. #include <utl/core/crtp.h>
  25. #include <utl/dev/dev_iterators.h>
  26. #include <utl/meta/meta.h>
  27. namespace utl {
  28. /*!
  29. * \ingroup Device Interface
  30. * \brief Abstract base classes for output devices
  31. */
  32. //!@{
  33. /*!
  34. * \brief
  35. * Template base class for output devices. using CRTP
  36. *
  37. * This class force a common interface for output devices.
  38. * By using this common interface the class implements
  39. * - Stream-like inserting operator
  40. * - Output iterator
  41. * - Const output iterator
  42. * to inherit to implementation.
  43. *
  44. * \param impl_t The CRTP type (the derived/implementation class typename).
  45. * \param data_t The devices base type of data
  46. * \param streamsize The number of elements to indicate eos.
  47. * \arg None or 0 Stream only. No iterator as begin() now equals end().
  48. */
  49. template <typename impl_t, typename data_t, size_t streamsize =0>
  50. class out_dev {
  51. _CRTP_IMPL(impl_t);
  52. using out_dev_t = out_dev <impl_t, data_t, streamsize>; //!< class type syntactic sugar
  53. //! Export types as input device concept demands
  54. //! @{
  55. public:
  56. using data_type = data_t;
  57. using pointer_type = data_t*;
  58. //!@}
  59. using type = out_dev_t; //!< Export type as identity meta-function
  60. /*!
  61. * \name Constructor / Destructor
  62. */
  63. //!@{
  64. protected:
  65. ~out_dev () = default; //!< \brief Allow destructor from derived only
  66. out_dev () = default; //!< \brief A default constructor from derived only
  67. out_dev(const out_dev_t&) = delete; //!< No copies
  68. out_dev_t& operator= (const out_dev_t&) = delete; //!< No copy assignments
  69. //!@}
  70. //! \name Common output device interface requirements
  71. //!@{
  72. private:
  73. size_t put_ (const data_t& data) { return impl().put_ (data); }
  74. size_t put_ (const data_t* data, size_t n) {
  75. return impl().put_ (data, n);
  76. }
  77. //!@}
  78. /*!
  79. * \name Common output device interface
  80. */
  81. //!@{
  82. public:
  83. /*!
  84. * \brief
  85. * Put interface. This function should send a single
  86. * data_t object to device.
  87. * \param data The data to send
  88. * \return The number of transmitted data items
  89. * \note
  90. * A successful call should return 1
  91. */
  92. size_t put (const data_t& data) {
  93. return put_ (data);
  94. }
  95. /*!
  96. * \brief
  97. * Put interface. This function should send a stream of
  98. * data_t objects to device.
  99. * \param data Pointer to buffer indenting write to device.
  100. * \param n The number of data of type data_t to send
  101. * \return The number of transmitted items.
  102. */
  103. size_t put (const data_t* data, size_t n) {
  104. return put_ (data, n);
  105. }
  106. //!@}
  107. /*!
  108. * \name Stream operator << interface
  109. */
  110. //!@{
  111. public:
  112. /*!
  113. * \brief
  114. * Template operator<< implementation for for all by value/ref parameters
  115. * \note
  116. * In the case _Src_t size is not an exact multiple of data_t size
  117. * the write data will be truncated and there may be data loss.
  118. * \param src Reference to source data
  119. * \return Reference to this device for chaining
  120. */
  121. template <typename _Src_t>
  122. out_dev_t& operator<< (_Src_t& src) {
  123. static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0),
  124. "Source size must be an integer multiple of device's data size");
  125. put_ (reinterpret_cast<data_t*>(&src), sizeof(_Src_t)/sizeof(data_t));
  126. return *this;
  127. }
  128. //! Overload to disallow pointer types as source
  129. template <typename _Src_t>
  130. out_dev_t& operator<< (_Src_t* src) = delete;
  131. //! Overload for single data_t object
  132. out_dev_t& operator<< (const data_t& src) {
  133. put_ (src);
  134. return *this;
  135. }
  136. //!@}
  137. /*!
  138. * \name STL-like Output iterator interface
  139. */
  140. //!@{
  141. using iterator = outdev_it <out_dev_t, data_t*, streamsize>; //!< Iterator
  142. using const_iterator = outdev_it <const out_dev_t, data_t*, streamsize>; //!< Const iterator
  143. //!@{ .begin implementation
  144. iterator begin () noexcept { return iterator(this, iterator::beg); }
  145. const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); }
  146. const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); }
  147. //!@}
  148. //!@{ .end implementation
  149. iterator end () noexcept { return iterator(this, iterator::eos); }
  150. const_iterator end () const noexcept { return const_iterator(this, iterator::eos); }
  151. const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); }
  152. //!@}
  153. //!@}
  154. };
  155. /*!
  156. * \brief
  157. * A virtual base class specialization
  158. * \param impl_t = virtual_tag
  159. * \param data_t The devices base type of data
  160. * \param streamsize The number of elements to indicate eos.
  161. * \arg None or 0 Stream only. No iterator as begin() now equals end().
  162. */
  163. template <typename data_t, size_t streamsize>
  164. class out_dev <virtual_tag, data_t, streamsize> {
  165. using out_dev_t = out_dev <virtual_tag, data_t, streamsize>; //!< class type syntactic sugar
  166. //! Export types as input device concept demands
  167. //! @{
  168. public:
  169. using data_type = data_t;
  170. using pointer_type = data_t*;
  171. //! @}
  172. using type = out_dev_t; //!< Export type as identity meta-function
  173. /*!
  174. * \name Constructor / Destructor
  175. */
  176. //!@{
  177. public:
  178. virtual ~out_dev () = default; //!< \brief Virtual destructor
  179. protected:
  180. out_dev () = default; //!< \brief A default constructor from derived only
  181. out_dev(const out_dev&) = delete; //!< No copies
  182. out_dev_t& operator= (const out_dev_t&) = delete; //!< No copy assignments
  183. //!@}
  184. /*!
  185. * \name Common output device interface requirements
  186. */
  187. //!@{
  188. private:
  189. /*!
  190. * \brief
  191. * Put interface. This function should send a single
  192. * data_t object to device.
  193. * \param data The data to send
  194. * \return The number of transmitted data items
  195. * \note
  196. * A successful call should return 1
  197. */
  198. virtual size_t put_ (const data_t& data) = 0;
  199. /*!
  200. * \brief
  201. * Put interface. This function should send a stream of
  202. * data_t objects to device.
  203. * \param data Pointer to buffer indenting write to device.
  204. * \param n The number of data of type data_t to send
  205. * \return The number of transmitted items.
  206. */
  207. virtual size_t put_ (const data_t* data, size_t n) = 0;
  208. //!@}
  209. /*!
  210. * \name Public Put interface
  211. */
  212. //!@{
  213. public:
  214. size_t put (const data_t& data) { return put_ (data); }
  215. size_t put (const data_t* data, size_t n) { return put_ (data, n); }
  216. //!@}
  217. /*!
  218. * \name Stream operator<< interface
  219. */
  220. //!@{
  221. public:
  222. /*!
  223. * \brief
  224. * Template operator<< implementation for for all by value/ref parameters
  225. * \param src Reference to source data
  226. * \return Reference to this device for chaining
  227. */
  228. template <typename _Src_t>
  229. out_dev_t& operator<< (_Src_t& src) {
  230. static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0),
  231. "Source size must be an integer multiple of device's data size");
  232. put_ (reinterpret_cast<data_t*>(&src), sizeof(_Src_t)/sizeof(data_t));
  233. return *this;
  234. }
  235. //! Overload to disallow pointer types as source
  236. template <typename _Src_t>
  237. out_dev_t& operator<< (_Src_t* src) = delete;
  238. //! Overload for single data_t object
  239. out_dev_t& operator<< (const data_t& src) {
  240. put_ (src);
  241. return *this;
  242. }
  243. //!@}
  244. /*!
  245. * \name STL-like Output iterator interface
  246. */
  247. //!@{
  248. using iterator = outdev_it <out_dev_t, data_t*, streamsize>; //!< Iterator
  249. using const_iterator = outdev_it <const out_dev_t, data_t*, streamsize>; //!< Const iterator
  250. //!@{ .begin implementation
  251. iterator begin () noexcept { return iterator(this, iterator::beg); }
  252. const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); }
  253. const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); }
  254. //!@}
  255. //!@{ .end implementation
  256. iterator end () noexcept { return iterator(this, iterator::eos); }
  257. const_iterator end () const noexcept { return const_iterator(this, iterator::eos); }
  258. const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); }
  259. //!@}
  260. //!@}
  261. };
  262. /*!
  263. * Output device concept
  264. */
  265. //! @{
  266. #if defined _utl_have_concepts
  267. template <typename _Tp>
  268. concept bool Out_dev = requires (_Tp t, const _Tp ct, typename _Tp::data_type v) {
  269. // Object type
  270. requires !std::is_copy_constructible<_Tp>::value;
  271. requires !std::is_copy_assignable<_Tp>::value;
  272. // Methods
  273. {t.put(v)} -> size_t;
  274. {t.put(&v, sizeof(v))} -> size_t;
  275. // Operators
  276. t << v;
  277. // Iterators
  278. typename _Tp::const_iterator;
  279. requires Outdev_it<typename _Tp::iterator>;
  280. requires Outdev_it<typename _Tp::const_iterator>;
  281. { t.begin() } -> typename _Tp::iterator;
  282. {ct.begin()} -> typename _Tp::const_iterator;
  283. { t.cbegin()} -> typename _Tp::const_iterator;
  284. { t.end() } -> typename _Tp::iterator;
  285. {ct.end()} -> typename _Tp::const_iterator;
  286. { t.cend()} -> typename _Tp::const_iterator;
  287. };
  288. #else
  289. namespace out_dev_details {
  290. using std::declval;
  291. template <class _Tp> using try_put1_t = decltype (declval<_Tp>().put (declval<const typename _Tp::data_type&>()));
  292. template <class _Tp> using try_put2_t = decltype (declval<_Tp>().put (declval<const typename _Tp::data_type*>(),
  293. declval<size_t>()));
  294. // operators
  295. //template <class _Tp> using try_insert_t= decltype (declval<_Tp>() << declval<typename _Tp::data_type&>());
  296. // iterator members
  297. template <class _Tp> using try_begin_t = decltype (declval<_Tp>().begin());
  298. template <class _Tp> using tryc_begin_t = decltype (declval<const _Tp>().begin());
  299. template <class _Tp> using try_cbegin_t = decltype (declval<const _Tp>().cbegin());
  300. template <class _Tp> using try_end_t = decltype (declval<_Tp>().begin());
  301. template <class _Tp> using tryc_end_t = decltype (declval<const _Tp>().begin());
  302. template <class _Tp> using try_cend_t = decltype (declval<const _Tp>().cend());
  303. //! Primary template to catch any non output device types
  304. template <typename _Tp, typename =void>
  305. struct is_out_dev_ : false_ { };
  306. //! template to catch a proper output device type
  307. template <typename _Tp>
  308. struct is_out_dev_ <_Tp,
  309. void_t <
  310. typename _Tp::data_type,
  311. typename _Tp::pointer_type,
  312. typename _Tp::iterator,
  313. typename _Tp::const_iterator,
  314. use_if_same_t <try_put1_t <_Tp>, size_t>,
  315. use_if_same_t <try_put2_t <_Tp>, size_t>,
  316. //if_same_t <try_insert_t<_Tp>,_Tp&>,
  317. use_if_same_t <try_begin_t<_Tp>, typename _Tp::iterator>,
  318. use_if_same_t <tryc_begin_t<_Tp>, typename _Tp::const_iterator>,
  319. use_if_same_t <try_cbegin_t<_Tp>, typename _Tp::const_iterator>,
  320. use_if_same_t <try_end_t<_Tp>, typename _Tp::iterator>,
  321. use_if_same_t <tryc_end_t<_Tp>, typename _Tp::const_iterator>,
  322. use_if_same_t <try_cend_t<_Tp>, typename _Tp::const_iterator>
  323. >
  324. > : true_ { };
  325. }
  326. /*!
  327. * Predicate for output device checking
  328. * \param _Tp Type to check
  329. * \return True if _Tp is a output device
  330. */
  331. template <typename _Tp>
  332. constexpr bool Out_dev = out_dev_details::is_out_dev_<_Tp>::value;
  333. #endif
  334. //!@}
  335. //!@}
  336. } //namespace utl
  337. #endif /* #ifndef __utl_dev_out_dev_h__ */