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.
 
 
 
 

341 lines
13 KiB

  1. /*!
  2. * \file utl/dev/in_dev.h
  3. * \brief Abstract base class interface for input 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_in_dev_h__
  22. #define __utl_dev_in_dev_h__
  23. #include <utl/impl/impl.h>
  24. #include <utl/helper/crtp.h>
  25. #include <utl/meta/sfinae.h>
  26. #include <utl/dev/dev_iterators.h>
  27. namespace utl {
  28. /*!
  29. * \ingroup Device Interface
  30. * \brief Abstract base class for input devices
  31. */
  32. //!@{
  33. /*!
  34. * \brief
  35. * Template base class for un-buffered input devices. using CRTP
  36. *
  37. * This class force a common interface for input, non-buffered devices.
  38. * By using this common interface the class implements
  39. * - Stream-like extracting operator
  40. * - Input iterator
  41. * - Const input 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 in_dev {
  51. _CRTP_IMPL(impl_t);
  52. using in_dev_t = in_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 = in_dev_t; //!< Export type as identity meta-function
  60. /*!
  61. * \name Constructor / Destructor
  62. */
  63. //!@{
  64. protected:
  65. ~in_dev () = default; //!< \brief Allow destructor from derived only
  66. in_dev () = default; //!< \brief A default constructor from derived only
  67. in_dev(const in_dev_t&) = delete; //!< No copies
  68. in_dev_t& operator= (const in_dev_t&) = delete; //!< No copy assignments
  69. //!@}
  70. //! \name Common input device interface requirements
  71. //! @{
  72. private:
  73. size_t get_ (data_t& data) { return impl().get_ (data); }
  74. size_t get_ (data_t* data, size_t n) { return impl().get (data, n); }
  75. //! @}
  76. /*!
  77. * \name Public Get interface
  78. */
  79. //!@{
  80. public:
  81. /*!
  82. * \brief
  83. * Get interface. This function should read
  84. * a single data_t object from device in blocking mode.
  85. * \param data Reference to data output from device.
  86. * \return Number of data read from device
  87. * \note
  88. * A successful call should return 1
  89. */
  90. size_t get (data_t& data) {
  91. return get_ (data);
  92. }
  93. /*!
  94. * \brief
  95. * Old stile get functionality using free standing data_t*.
  96. * This function should return a stream of data from device
  97. * \param data Pointer to buffer to write the data from device.
  98. * \param n The number of data of type data_t to read
  99. * \return The read data items.
  100. */
  101. size_t get (data_t* data, size_t n) {
  102. return get_ (data, n);
  103. }
  104. //!@}
  105. /*!
  106. * \name Stream operator >> interface
  107. */
  108. //!@{
  109. public:
  110. /*!
  111. * \brief
  112. * Template operator >> implementation for for all by value/ref parameters
  113. * \note
  114. * In the case _Dst_t size is not a integer multiple of device's data size
  115. * this will fail by static assertion
  116. * \param dst Reference to destination
  117. * \return Reference to this device for chaining
  118. */
  119. template <typename _Dst_t>
  120. in_dev_t& operator>> (_Dst_t& dst) {
  121. static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0),
  122. "Target size must be an integer multiple of device's data size");
  123. get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t));
  124. return *this;
  125. }
  126. //! Specialization to disallow pointer types as destination
  127. template <typename _Dst_t>
  128. in_dev_t& operator>> (_Dst_t* dst) = delete;
  129. //! Overload for single data_t object
  130. in_dev_t& operator>> (data_t& dst) {
  131. get_ (dst);
  132. return *this;
  133. }
  134. //!@}
  135. /*!
  136. * \name STL-like Input iterator interface
  137. */
  138. //!@{
  139. using iterator = indev_it <in_dev_t, data_t*, streamsize>; //!< Iterator
  140. using const_iterator = indev_it <in_dev_t, const data_t*, streamsize>; //!< Const iterator
  141. //!@{ .begin implementation
  142. iterator begin () noexcept { return iterator(this, iterator::init); }
  143. const_iterator begin () const noexcept { return const_iterator(this, iterator::init); }
  144. const_iterator cbegin () const noexcept { return const_iterator(this, iterator::init); }
  145. //!@}
  146. //!@{ .end implementation
  147. iterator end () noexcept { return iterator(this, iterator::eos); }
  148. const_iterator end () const noexcept { return const_iterator(this, iterator::eos); }
  149. const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); }
  150. //!@}
  151. //!@}
  152. };
  153. /*!
  154. * \brief
  155. * A virtual base class specialization
  156. * \param impl_t = virtual_tag
  157. * \param data_t The devices base type of data
  158. * \param streamsize_t Type to hold the number of read bytes
  159. */
  160. template <typename data_t, size_t streamsize>
  161. class in_dev <virtual_tag, data_t, streamsize> {
  162. //!< class type syntactic sugar
  163. using in_dev_t = in_dev <virtual_tag, data_t, streamsize>;
  164. //! Export types as input device concept demands
  165. //!@{
  166. public:
  167. using data_type = data_t;
  168. using pointer_type = data_t*;
  169. //!@}
  170. using type = in_dev_t; //!< Export type as identity meta-function
  171. /*!
  172. * \name Constructor / Destructor
  173. */
  174. //!@{
  175. public:
  176. virtual ~in_dev () = default; //!< \brief Virtual destructor
  177. protected:
  178. in_dev () = default; //!< \brief A default constructor from derived only
  179. in_dev(const in_dev_t&) = delete; //!< No copies
  180. in_dev_t& operator= (const in_dev_t&) = delete; //!< No copy assignments
  181. //!@}
  182. /*!
  183. * \name Common input device interface requirements
  184. */
  185. //!@{
  186. private:
  187. /*!
  188. * \brief
  189. * Get interface. This function should read
  190. * a single data_t object from device in blocking mode.
  191. * \param data Reference to data output from device.
  192. * \return Number of data read from device
  193. * \note
  194. * A successful call should return 1
  195. */
  196. virtual size_t get_ (data_t& data) = 0;
  197. /*!
  198. * \brief
  199. * Old stile get functionality using free standing data_t*.
  200. * This function should return a stream of data from device
  201. * \param data Pointer to buffer to write the data from device.
  202. * \param n The number of data of type data_t to read
  203. * \return The read data items.
  204. */
  205. virtual size_t get_ (data_t* data, size_t n) = 0;
  206. //!@}
  207. /*!
  208. * \name Public Get interface
  209. */
  210. //!@{
  211. public:
  212. size_t get (data_t& data) { return get_ (data); }
  213. size_t get (data_t* data, size_t n) { return get_ (data, n); }
  214. //!@}
  215. /*!
  216. * \name Stream operator >> interface
  217. */
  218. //!@{
  219. public:
  220. /*!
  221. * \brief
  222. * Template operator >> implementation for for all by value/ref parameters
  223. * \note
  224. * In the case _Dst_t size is not a integer multiple of device's data size
  225. * this will fail by static assertion
  226. * \param dst Reference to destination
  227. * \return Reference to this device for chaining
  228. */
  229. template <typename _Dst_t>
  230. in_dev_t& operator>> (_Dst_t& dst) {
  231. static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0),
  232. "Target size must be an integer multiple of device's data size");
  233. get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t));
  234. return *this;
  235. }
  236. //! specialization to disallow pointer types as destination
  237. template <typename _Dst_t>
  238. in_dev_t& operator>> (_Dst_t* dst) = delete;
  239. //! Overload for single data_t object
  240. in_dev_t& operator>> (data_t& dst) {
  241. get_ (dst);
  242. return *this;
  243. }
  244. //!@}
  245. /*!
  246. * \name STL-like Input iterator interface
  247. */
  248. //!@{
  249. using iterator = indev_it <in_dev_t, data_t*, streamsize>; //!< Iterator
  250. using const_iterator = indev_it <in_dev_t, const data_t*, streamsize>; //!< Const iterator
  251. //!@{ .begin implementation
  252. iterator begin () noexcept { return iterator(this, iterator::init); }
  253. const_iterator begin () const noexcept { return const_iterator(this, iterator::init); }
  254. const_iterator cbegin () const noexcept { return const_iterator(this, iterator::init); }
  255. //!@}
  256. //!@{ .end implementation
  257. iterator end () noexcept { return iterator(this, iterator::eos); }
  258. const_iterator end () const noexcept { return const_iterator(this, iterator::eos); }
  259. const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); }
  260. //!@}
  261. //!@}
  262. };
  263. /*
  264. * Input device predicate (concept)
  265. */
  266. namespace in_dev_details {
  267. using std::declval;
  268. // main api members
  269. template <class _Tp> using try_get1_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type&>()));
  270. template <class _Tp> using try_get2_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type*>(), declval<size_t>()));
  271. // operators
  272. //template <class _Tp> using try_extract_t= decltype (declval<_Tp>() >> declval<typename _Tp::data_type&>());
  273. // iterator members
  274. template <class _Tp> using try_begin_t = decltype (declval<_Tp>().begin());
  275. template <class _Tp> using tryc_begin_t = decltype (declval<const _Tp>().begin());
  276. template <class _Tp> using try_cbegin_t = decltype (declval<const _Tp>().cbegin());
  277. template <class _Tp> using try_end_t = decltype (declval<_Tp>().begin());
  278. template <class _Tp> using tryc_end_t = decltype (declval<const _Tp>().begin());
  279. template <class _Tp> using try_cend_t = decltype (declval<const _Tp>().cend());
  280. //! Primary template to catch any non input device types
  281. template <typename _Tp, typename =void>
  282. struct is_in_dev_ : false_ {};
  283. //! template to catch a proper input device type
  284. template <typename _Tp>
  285. struct is_in_dev_ <_Tp,
  286. void_t <
  287. typename _Tp::data_type,
  288. typename _Tp::pointer_type,
  289. typename _Tp::iterator,
  290. typename _Tp::const_iterator,
  291. use_if_same_t <try_get1_t <_Tp>, size_t>,
  292. use_if_same_t <try_get2_t <_Tp>, size_t>,
  293. //if_same_t <try_extract_t<_Tp>,typename _Tp&>,
  294. use_if_same_t <try_begin_t<_Tp>, typename _Tp::iterator>,
  295. use_if_same_t <tryc_begin_t<_Tp>, typename _Tp::const_iterator>,
  296. use_if_same_t <try_cbegin_t<_Tp>, typename _Tp::const_iterator>,
  297. use_if_same_t <try_end_t<_Tp>, typename _Tp::iterator>,
  298. use_if_same_t <tryc_end_t<_Tp>, typename _Tp::const_iterator>,
  299. use_if_same_t <try_cend_t<_Tp>, typename _Tp::const_iterator>
  300. >
  301. > : true_ {};
  302. }
  303. /*!
  304. * Predicate for input device checking
  305. * \param _Tp Type to check
  306. * \return True if _Tp is a input device
  307. */
  308. template <typename _Tp>
  309. constexpr bool In_dev = in_dev_details::is_in_dev_ <_Tp>::value;
  310. }
  311. //!@}
  312. #endif /* #ifndef __utl_dev_in_dev_h__ */