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.
 
 
 
 

531 lines
19 KiB

  1. /*!
  2. * \file utl/dev/idx_dev.h
  3. * \brief Abstract base class implementations for indexed
  4. * devices interface of utl
  5. *
  6. * Copyright (C) 2018 Christos Choutouridis
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Lesser General Public License as
  10. * published by the Free Software Foundation, either version 3
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #ifndef __utl_dev_idx_dev_h__
  23. #define __utl_dev_idx_dev_h__
  24. #include <utl/impl/impl.h>
  25. #include <utl/helper/crtp.h>
  26. #include <utl/meta/sfinae.h>
  27. #include <utl/dev/dev_iterators.h>
  28. namespace utl {
  29. /*!
  30. * \ingroup Device Interface
  31. * \brief Abstract base class for indexed devices
  32. */
  33. //!@{
  34. /*!
  35. * \brief
  36. * Template base class for indexed (I/O) devices. using CRTP
  37. *
  38. * This class force a common interface for input, indexed (I/O) devices.
  39. * By using this common interface the class implements
  40. * - Stream-like extracting and insertion operator
  41. * - Helper operators
  42. * - Input iterator
  43. * - Const input iterator
  44. * to inherit to implementation.
  45. *
  46. * \param impl_t The CRTP type (the derived/implementation class typename).
  47. * \param data_t The devices base type of data
  48. * \param idx_t The type to use for indexing
  49. * \param streamsize The number of elements to indicate eos.
  50. */
  51. template <typename impl_t, typename data_t, typename idx_t, size_t N>
  52. class idx_dev {
  53. _CRTP_IMPL(impl_t);
  54. using idx_dev_t = idx_dev <impl_t, data_t, idx_t, N>; //!< class type syntactic sugar
  55. //!@{ Export types as index device concept demands
  56. public:
  57. using data_type = data_t;
  58. using pointer_type= data_t*;
  59. using idx_type = idx_t;
  60. //!@}
  61. using type = idx_dev_t; //!< Export type as identity meta-function
  62. /*!
  63. * \name Constructor / Destructor
  64. */
  65. //!@{
  66. protected:
  67. ~idx_dev () = default; //!< \brief Allow destructor from derived only
  68. idx_dev () = default; //!< \brief A default constructor from derived only
  69. idx_dev(const idx_dev_t&) = delete; //!< No copies
  70. idx_dev_t& operator= (const idx_dev_t&) = delete; //!< No copy assignments
  71. //!@}
  72. //! \name Common index device interface requirements
  73. //!@{
  74. private:
  75. size_t get_ (data_t& data, idx_t idx) { return impl().get_(data, idx); }
  76. size_t get_ (data_t* data, size_t n, idx_t idx) { return impl().get_(data, n, idx); }
  77. size_t put_ (const data_t& data, idx_t idx) { return impl().put_(data, idx); }
  78. size_t put_ (const data_t* data, size_t n, idx_t idx) { return impl().put_ (data, n, idx); }
  79. idx_t cursor_ () const { return impl().cursor_(); }
  80. void cursor_ (idx_t idx) { impl().cursor_(idx); }
  81. //!@}
  82. /*!
  83. * \name Public index device interface
  84. */
  85. //!@{
  86. public:
  87. /*!
  88. * \brief
  89. * Get interface. This function should read
  90. * a single data_t object from device in blocking mode.
  91. * \param data Reference to data output from device.
  92. * \return Number of data read from device
  93. * \note
  94. * A successful call should return 1
  95. */
  96. size_t get (data_t& data, idx_t cursor) {
  97. return get_ (data, cursor);
  98. }
  99. /*!
  100. * \brief
  101. * Old stile get functionality using free standing data_t*.
  102. * This function should return a stream of data from device
  103. * \param data Pointer to buffer to write the data from device.
  104. * \param n The number of data of type data_t to read
  105. * \return The read data items.
  106. */
  107. size_t get (data_t* data, size_t n, idx_t cursor) {
  108. return get_ (data, n, cursor);
  109. }
  110. /*!
  111. * \brief
  112. * Put interface. This function should send a single
  113. * data_t object to device.
  114. * \param data The data to send
  115. * \return The number of transmitted data items
  116. * \note
  117. * A successful call should return 1
  118. */
  119. size_t put (const data_t& data, idx_t cursor) {
  120. return put_ (data, cursor);
  121. }
  122. /*!
  123. * \brief
  124. * Put interface. This function should send a stream of
  125. * data_t objects to device.
  126. * \param data Pointer to buffer indenting write to device.
  127. * \param n The number of data of type data_t to send
  128. * \return The number of transmitted items.
  129. */
  130. size_t put (const data_t* data, size_t n, idx_t cursor) {
  131. return put_ (data, n, cursor);
  132. }
  133. /*!
  134. * \brief
  135. * Return the current cursor position
  136. */
  137. idx_t cursor () const { return cursor_(); }
  138. /*!
  139. * \brief
  140. * Set the cursor position
  141. * \param idx Cursor address to set
  142. * \return The current cursor
  143. */
  144. idx_t cursor (idx_t idx) { return cursor_(idx); }
  145. //!@}
  146. /*!
  147. * \name Stream operator >> interface
  148. */
  149. //!@{
  150. /*!
  151. * \brief
  152. * Template operator>> implementation for for all by value/ref parameters
  153. * \param dst Reference to destination
  154. * \return Reference to this device for chaining
  155. */
  156. template <typename _Dst_t>
  157. idx_dev_t& operator>> (_Dst_t& dst) {
  158. static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0),
  159. "Target size must be a integer multiple of device's data size");
  160. get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t), cursor_());
  161. return *this;
  162. }
  163. //! Specialization to disallow pointer types as destination
  164. template <typename _Dst_t>
  165. idx_dev_t& operator>> (_Dst_t* dst) = delete;
  166. //! Overload for single data_t object
  167. idx_dev_t& operator>> (data_t& dst) {
  168. get_ (dst, cursor_());
  169. return *this;
  170. }
  171. //!@}
  172. /*!
  173. * \name Stream operator<< interface
  174. */
  175. //!@{
  176. /*!
  177. * \brief
  178. * Template operator<< implementation for for all by value/ref parameters
  179. * \param src Reference to source data
  180. * \return Reference to this device for chaining
  181. */
  182. template <typename _Src_t>
  183. idx_dev_t& operator<< (_Src_t& src) {
  184. static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0),
  185. "Source size must be a integer multiple of device's data size");
  186. put_ (reinterpret_cast<data_t*>(&src), sizeof (_Src_t)/sizeof(data_t), cursor_());
  187. return *this;
  188. }
  189. //! specialization to disallow pointer types as source
  190. template <typename _Src_t>
  191. idx_dev_t& operator<< (_Src_t* src) = delete;
  192. //! Overload for single data_t object
  193. idx_dev_t& operator<< (const data_t& src) {
  194. put_ (src, cursor_());
  195. return *this;
  196. }
  197. //!@}
  198. /*!
  199. * \name Helper operators
  200. */
  201. //!@{
  202. data_t& operator[] (const idx_t idx) {
  203. iterator it(this, idx);
  204. return *it;
  205. }
  206. //!@}
  207. /*!
  208. * \name STL-like Input iterator interface
  209. */
  210. //!@{
  211. public:
  212. using iterator = idxdev_it <idx_dev_t, data_t*, N>; //!< Iterator
  213. using const_iterator = idxdev_it <idx_dev_t, const data_t*, N>; //!< Const iterator
  214. //!@{ .begin implementation
  215. iterator begin () noexcept { return iterator(this, iterator::beg); }
  216. const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); }
  217. const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); }
  218. //!@}
  219. //!@{ .end implementation
  220. iterator end () noexcept { return iterator(this, iterator::eos); }
  221. const_iterator end () const noexcept { return const_iterator(this, iterator::eos); }
  222. const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); }
  223. //!@}
  224. //!@}
  225. };
  226. /*!
  227. * \brief
  228. * A virtual base class specialization
  229. * \param impl_t = virtual_tag
  230. * \param data_t The devices base type of data
  231. * \param idx_t The type to use for indexing
  232. * \param streamsize The number of elements to indicate eos.
  233. */
  234. template <typename data_t, typename idx_t, size_t N>
  235. class idx_dev <virtual_tag, data_t, idx_t, N> {
  236. using idx_dev_t = idx_dev <virtual_tag, data_t, idx_t, N>; //!< class type syntactic sugar
  237. //!@{ Export types as index device concept demands
  238. public:
  239. using data_type = data_t;
  240. using pointer_type= data_t*;
  241. using idx_type = idx_t;
  242. //!@}
  243. using type = idx_dev_t; //!< Export type as identity meta-function
  244. /*!
  245. * \name Constructor / Destructor
  246. */
  247. //!@{
  248. public:
  249. virtual ~idx_dev () = default; //!< \brief Virtual destructor
  250. protected:
  251. idx_dev () = default; //!< \brief A default constructor from derived only
  252. idx_dev(const idx_dev_t&) = delete; //!< No copies
  253. idx_dev_t& operator= (const idx_dev_t&) = delete; //!< No copy assignments
  254. //!@}
  255. //!@{ \name Common index device interface requirements
  256. private:
  257. virtual size_t get_ (data_t&, idx_t) =0;
  258. virtual size_t get_ (data_t*, size_t n, idx_t) =0;
  259. virtual size_t put_ (const data_t&, idx_t) =0;
  260. virtual size_t put_ (const data_t*, size_t n, idx_t) =0;
  261. virtual idx_t cursor_ () const =0;
  262. virtual void cursor_ (idx_t) =0;
  263. //!@}
  264. /*!
  265. * \name Public index device interface
  266. */
  267. //!@{
  268. public:
  269. /*!
  270. * \brief
  271. * Get interface. This function should read
  272. * a single data_t object from device in blocking mode.
  273. * \param data Reference to data output from device.
  274. * \return Number of data read from device
  275. * \note
  276. * A successful call should return 1
  277. */
  278. size_t get (data_t& data, idx_t cursor) {
  279. return get_ (data, cursor);
  280. }
  281. /*!
  282. * \brief
  283. * Old stile get functionality using free standing data_t*.
  284. * This function should return a stream of data from device
  285. * \param data Pointer to buffer to write the data from device.
  286. * \param n The number of data of type data_t to read
  287. * \return The read data items.
  288. */
  289. size_t get (data_t* data, size_t n, idx_t cursor) {
  290. return get_ (data, n, cursor);
  291. }
  292. /*!
  293. * \brief
  294. * Put interface. This function should send a single
  295. * data_t object to device.
  296. * \param data The data to send
  297. * \return The number of transmitted data items
  298. * \note
  299. * A successful call should return 1
  300. */
  301. size_t put (const data_t& data, idx_t cursor) {
  302. return put_ (data, cursor);
  303. }
  304. /*!
  305. * \brief
  306. * Put interface. This function should send a stream of
  307. * data_t objects to device.
  308. * \param data Pointer to buffer indenting write to device.
  309. * \param n The number of data of type data_t to send
  310. * \return The number of transmitted items.
  311. */
  312. size_t put (const data_t* data, size_t n, idx_t cursor) {
  313. return put_ (data, n, cursor);
  314. }
  315. /*!
  316. * \brief
  317. * Return the current cursor position
  318. */
  319. idx_t cursor () const { return cursor_(); }
  320. /*!
  321. * \brief
  322. * Set the cursor position
  323. * \param idx Cursor address to set
  324. * \return The current cursor
  325. */
  326. idx_t cursor (idx_t idx) { return cursor_(idx); }
  327. //!@}
  328. /*!
  329. * \name Stream operator>> interface
  330. */
  331. //!@{
  332. /*!
  333. * \brief
  334. * Template operator>> implementation for for all by value/ref parameters
  335. * \param dst Reference to destination
  336. * \return Reference to this device for chaining
  337. */
  338. template <typename _Dst_t>
  339. idx_dev_t& operator>> (_Dst_t& dst) {
  340. static_assert ((sizeof (_Dst_t)%sizeof(data_t) == 0),
  341. "Target size must be an integer multiple of device's data size");
  342. get_ (reinterpret_cast<data_t*>(&dst), sizeof(_Dst_t)/sizeof(data_t), cursor_());
  343. return *this;
  344. }
  345. //! specialization to disallow pointer types as destination
  346. template <typename _Dst_t>
  347. idx_dev_t& operator>> (_Dst_t* dst) = delete;
  348. //! Overload for single data_t object
  349. idx_dev_t& operator>> (data_t& dst) {
  350. get_ (dst, cursor_());
  351. return *this;
  352. }
  353. //!@}
  354. /*!
  355. * \name Stream operator<< interface
  356. */
  357. //!@{
  358. /*!
  359. * \brief
  360. * Template operator<< implementation for for all by value/ref parameters
  361. * \param src Reference to source data
  362. * \return Reference to this device for chaining
  363. */
  364. template <typename _Src_t>
  365. idx_dev_t& operator<< (_Src_t& src) {
  366. static_assert ((sizeof (_Src_t)%sizeof(data_t) == 0),
  367. "Source size must be an integer multiple of device's data size");
  368. put_ (reinterpret_cast<data_t*>(&src), sizeof (_Src_t)/sizeof(data_t), cursor_());
  369. return *this;
  370. }
  371. //! specialization to disallow pointer types as source
  372. template <typename _Src_t>
  373. idx_dev_t& operator<< (_Src_t* src) = delete;
  374. //! Overload for single data_t object
  375. idx_dev_t& operator<< (const data_t& src) {
  376. put_ (src, cursor_());
  377. return *this;
  378. }
  379. //!@}
  380. /*!
  381. * \name Helper operators
  382. */
  383. //!@{
  384. data_t& operator[] (const idx_t idx) {
  385. iterator it(this, idx);
  386. return *it;
  387. }
  388. //!@}
  389. /*!
  390. * \name STL-like Input iterator interface
  391. */
  392. //!@{
  393. public:
  394. using iterator = idxdev_it <idx_dev_t, data_t*, N>; //!< Iterator
  395. using const_iterator = idxdev_it <idx_dev_t, const data_t*, N>; //!< Const iterator
  396. //!@{ .begin implementation
  397. iterator begin () noexcept { return iterator(this, iterator::beg); }
  398. const_iterator begin () const noexcept { return const_iterator(this, iterator::beg); }
  399. const_iterator cbegin () const noexcept { return const_iterator(this, iterator::beg); }
  400. //!@}
  401. //!@{ .end implementation
  402. iterator end () noexcept { return iterator(this, iterator::eos); }
  403. const_iterator end () const noexcept { return const_iterator(this, iterator::eos); }
  404. const_iterator cend () const noexcept { return const_iterator(this, iterator::eos); }
  405. //!@}
  406. //!@}
  407. };
  408. /*!
  409. * indexed device concept
  410. */
  411. //! @{
  412. #if defined _utl_have_concepts
  413. template <typename _Tp>
  414. concept bool Idx_dev = requires (_Tp t, const _Tp ct, typename _Tp::data_type v) {
  415. // Object type
  416. // requires std::is_default_constructible<_Tp>::value;
  417. requires !std::is_copy_constructible<_Tp>::value;
  418. requires !std::is_copy_assignable<_Tp>::value;
  419. // Methods
  420. {t.get(v, 0)} -> size_t;
  421. {t.get(&v, 1, 0)} -> size_t;
  422. {t.put(v, 0)} -> size_t;
  423. {t.put(&v, 1, 0)} -> size_t;
  424. // Operators
  425. t >> v;
  426. t << v;
  427. {t[typename _Tp::idx_type{}]} -> typename _Tp::data_type&;
  428. // Iterators
  429. requires idxdev_iterator_c<typename _Tp::iterator>;
  430. typename _Tp::const_iterator; //XXX: change to concept: is_idxdev_iterator<_Tp>
  431. //requires idxdev_iterator_c<typename _Tp::const_iterator>;
  432. { t.begin()} -> typename _Tp::iterator;
  433. // {ct.begin()} -> typename _Tp::const_iterator;
  434. // { t.cbegin()} -> typename _Tp::const_iterator;
  435. { t.end()} -> typename _Tp::iterator;
  436. // {ct.end()} -> typename _Tp::const_iterator;
  437. // { t.cend()} -> typename _Tp::const_iterator;
  438. };
  439. #else
  440. namespace idx_dev_details {
  441. using std::declval;
  442. // main api members
  443. template <class _Tp> using try_get1_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type&>()));
  444. template <class _Tp> using try_get2_t = decltype (declval<_Tp>().get (declval<typename _Tp::data_type*>(), declval<size_t>()));
  445. // operators
  446. //template <class _Tp> using try_extract_t= decltype (declval<_Tp>() >> declval<typename _Tp::data_type&>());
  447. // iterator members
  448. template <class _Tp> using try_begin_t = decltype (declval<_Tp>().begin());
  449. template <class _Tp> using tryc_begin_t = decltype (declval<const _Tp>().begin());
  450. template <class _Tp> using try_cbegin_t = decltype (declval<const _Tp>().cbegin());
  451. template <class _Tp> using try_end_t = decltype (declval<_Tp>().begin());
  452. template <class _Tp> using tryc_end_t = decltype (declval<const _Tp>().begin());
  453. template <class _Tp> using try_cend_t = decltype (declval<const _Tp>().cend());
  454. //! Primary template to catch any non input device types
  455. template <typename _Tp, typename =void>
  456. struct is_idx_dev_ : false_ {};
  457. //! template to catch a proper input device type
  458. template <typename _Tp>
  459. struct is_idx_dev_ <_Tp,
  460. void_t <
  461. typename _Tp::data_type,
  462. typename _Tp::pointer_type,
  463. typename _Tp::iterator,
  464. typename _Tp::const_iterator,
  465. use_if_same_t <try_get1_t <_Tp>, size_t>,
  466. use_if_same_t <try_get2_t <_Tp>, size_t>,
  467. //if_same_t <try_extract_t<_Tp>,typename _Tp&>,
  468. use_if_same_t <try_begin_t<_Tp>, typename _Tp::iterator>,
  469. use_if_same_t <tryc_begin_t<_Tp>, typename _Tp::const_iterator>,
  470. use_if_same_t <try_cbegin_t<_Tp>, typename _Tp::const_iterator>,
  471. use_if_same_t <try_end_t<_Tp>, typename _Tp::iterator>,
  472. use_if_same_t <tryc_end_t<_Tp>, typename _Tp::const_iterator>,
  473. use_if_same_t <try_cend_t<_Tp>, typename _Tp::const_iterator>
  474. >
  475. > : true_ {};
  476. }
  477. /*!
  478. * Predicate for input device checking
  479. * \param _Tp Type to check
  480. * \return True if _Tp is a input device
  481. */
  482. template <typename _Tp>
  483. constexpr bool Idx_dev = idx_dev_details::is_idx_dev_ <_Tp>::value;
  484. #endif
  485. //!@}
  486. }
  487. //!@}
  488. #endif /* #ifndef __utl_dev_idx_dev_h__ */