Micro template library A library for building device drivers
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

300 lignes
12 KiB

  1. /*!
  2. * \file container/edeque.h
  3. * \brief
  4. * A deque with event based callables
  5. *
  6. * \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
  7. *
  8. * <dl class=\"section copyright\"><dt>License</dt><dd>
  9. * The MIT License (MIT)
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in all
  19. * copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  27. * SOFTWARE.
  28. * </dd></dl>
  29. */
  30. #ifndef utl_container_edeque_h__
  31. #define utl_container_edeque_h__
  32. #include <utl/core/impl.h>
  33. #include <utl/container/deque.h>
  34. #include <functional>
  35. #include <utility>
  36. #include <type_traits>
  37. namespace utl {
  38. /*!
  39. * \class edeque
  40. * \brief
  41. * A statically allocated deque with size and data matching event based callables.
  42. *
  43. * The edeque inherits deque and provide the callable functionality as a wrapper.
  44. *
  45. * There are two types of events.
  46. * - Size based events, which are cleared as soon as they served. These events are checked
  47. * every time the deque change its size. If the criteria match we call the callable of type
  48. * \c Fn
  49. * - Data based events, which are permanently. These events are checked every time an item is
  50. * pushed or popped from the deque. If the criteria match we call call the callable of type
  51. * \c Fn
  52. *
  53. * \tparam Data_t The char-like queued item type. Usually \c char
  54. * \tparam N The size of edeque
  55. * \tparam SemiAtomic True for semi-atomic operation. In that case the \c ring_iterator is also atomic.
  56. * \tparam Fn The type of Callable
  57. * \note
  58. * SemiAtomic means it is safe to access different ends from different threads. For example one thread can
  59. * push only from front and another can pop from back to implement a queue.
  60. */
  61. template <typename Data_t, size_t N, bool SemiAtomic =false, typename Fn = std::function<void()>>
  62. class edeque : public deque<Data_t, N, SemiAtomic> {
  63. public:
  64. // meta-identity types
  65. using type = edeque<Data_t, N, SemiAtomic, Fn>;
  66. using base_type = deque<Data_t, N, SemiAtomic>;
  67. using callable_t = Fn;
  68. using range_t = typename base_type::range_t;
  69. // STL
  70. using value_type = typename base_type::value_type;
  71. using reference = typename base_type::reference;
  72. using const_reference = typename base_type::const_reference;
  73. using pointer = typename base_type::pointer;
  74. using const_pointer = typename base_type::const_pointer;
  75. using iterator = typename base_type::iterator;
  76. using const_iterator = typename base_type::const_iterator;
  77. using reverse_iterator = typename base_type::reverse_iterator;
  78. using const_reverse_iterator= typename base_type::const_reverse_iterator;
  79. //! \name Public types
  80. //! @{
  81. public:
  82. //! \enum match_mode
  83. //! The mode of match operation
  84. enum class match_mode { SIZE, DATA };
  85. //! \enum size_match
  86. //! The type of matching for size based match
  87. enum class size_match { DISABLED =0, EQ, NE, LT, LE, GT, GE };
  88. //! \enum data_match
  89. //! The type of matching for data based match
  90. enum class data_match { DISABLED =0, MATCH_PUSH, MATCH_POP, MISMATCH_PUSH, MISMATCH_POP};
  91. // TODO: trigger mode for one-shot or repeated functionality
  92. // enum class trigger_mode { ONE_SHOT, REPEATED };
  93. //! \struct size_trigger
  94. //! Size trigger data type
  95. struct size_trigger {
  96. size_match type;
  97. size_t size;
  98. };
  99. //! \struct data_trigger
  100. //! Data trigger data type
  101. struct data_trigger {
  102. data_match type;
  103. Data_t value;
  104. };
  105. //! \union trigger
  106. //! \brief
  107. //! A union for the common types.
  108. //! There is only one mode. Either "size" with \ref size_match type and a size to match,
  109. //! or "data" with \ref data_match type and a value to match
  110. union trigger {
  111. size_trigger tsize;
  112. data_trigger tdata;
  113. };
  114. //! @}
  115. //! \name Constructor / Destructor
  116. //! @{
  117. public:
  118. //! Default constructor
  119. constexpr edeque () noexcept :
  120. base_type() { }
  121. //! Size trigger constructor
  122. constexpr edeque (size_match match, size_t size, callable_t&& fn) noexcept :
  123. base_type(),
  124. mode_{match_mode::SIZE},
  125. callback_{std::forward<callable_t>(fn)} {
  126. trigger_.tsize.type = match;
  127. trigger_.tsize.size = size;
  128. }
  129. //! Data trigger constructor
  130. constexpr edeque (data_match match, Data_t value, callable_t&& fn) noexcept :
  131. base_type(),
  132. mode_{match_mode::DATA},
  133. callback_{std::forward<callable_t>(fn)} {
  134. trigger_.tdata.type = match;
  135. trigger_.tdata.value = value;
  136. }
  137. //! @}
  138. //! \name Public interface
  139. //! @{
  140. //! \brief
  141. //! Manually checks the size trigger and calls it we have match.
  142. //! \return True if the callable has called.
  143. bool check_trigger () noexcept {
  144. return check_trigger_size_();
  145. }
  146. //! \brief
  147. //! Manually set (or alters) the \c size trigger. This function does not fire the
  148. //! \ref check_trigger()
  149. //! \param match The match type
  150. //! \param size The size for with we check against
  151. //! \param fn The callable to call on match
  152. void set_trigger (size_match match, size_t size, callable_t&& fn) noexcept {
  153. mode_ = match_mode::SIZE;
  154. trigger_.tsize.type = match;
  155. trigger_.tsize.size = size;
  156. callback_ = std::forward<callable_t>(fn);
  157. }
  158. //! \brief
  159. //! Manually set (or alters) the \c data trigger. This function does not fire the
  160. //! \ref check_trigger()
  161. //! \param match The match type
  162. //! \param value The value for with we check against
  163. //! \param fn The callable to call on match
  164. void set_trigger (data_match match, Data_t value, callable_t&& fn) noexcept {
  165. mode_ = match_mode::DATA;
  166. trigger_.tdata.type = match;
  167. trigger_.tdata.value= value;
  168. callback_ = std::forward<callable_t>(fn);
  169. }
  170. //! \brief Manually clears the trigger
  171. void clear_trigger () noexcept {
  172. mode_ = match_mode{};
  173. trigger_ = trigger{};
  174. callback_ = callable_t{};
  175. }
  176. //! @}
  177. //! \name Base class uses and overwrites
  178. //! @{
  179. void push_front (const Data_t& it) noexcept {
  180. base_type::push_front(it);
  181. check_trigger_push_async_(it);
  182. }
  183. void push_back (const Data_t& it) noexcept {
  184. base_type::push_back(it);
  185. check_trigger_push_async_(it);
  186. }
  187. Data_t pop_front () noexcept {
  188. Data_t t = base_type::pop_front();
  189. check_trigger_pop_async_(t);
  190. return t;
  191. }
  192. Data_t pop_back () noexcept {
  193. Data_t t = base_type::pop_back();
  194. check_trigger_pop_async_(t);
  195. return t;
  196. }
  197. //! @}
  198. //! \name Private functionality
  199. //! @{
  200. private:
  201. //! \brief
  202. //! Manually checks the size trigger and calls it we have match.
  203. //! \return True if the callable has called.
  204. bool check_trigger_size_ () {
  205. bool match;
  206. switch (trigger_.tsize.type) {
  207. default:
  208. case size_match::DISABLED: match = false; break;
  209. case size_match::EQ: match = (base_type::size() == trigger_.tsize.size); break;
  210. case size_match::NE: match = (base_type::size() != trigger_.tsize.size); break;
  211. case size_match::LT: match = (base_type::size() < trigger_.tsize.size); break;
  212. case size_match::LE: match = (base_type::size() <= trigger_.tsize.size); break;
  213. case size_match::GT: match = (base_type::size() > trigger_.tsize.size); break;
  214. case size_match::GE: match = (base_type::size() >= trigger_.tsize.size); break;
  215. }
  216. if (match) {
  217. callback_();
  218. clear_trigger();
  219. }
  220. return match;
  221. }
  222. //! \brief
  223. //! Manually checks the data trigger on push and calls it we have match.
  224. //! \param it The item to check against
  225. //! \return True if the callable has called.
  226. bool check_trigger_push_value_ (const Data_t& it) {
  227. bool match;
  228. switch (trigger_.tdata.type) {
  229. default:
  230. case data_match::DISABLED: match = false; break;
  231. case data_match::MATCH_PUSH: match = (it == trigger_.tdata.value); break;
  232. case data_match::MISMATCH_PUSH: match = (it != trigger_.tdata.value); break;
  233. }
  234. if (match)
  235. callback_();
  236. return match;
  237. }
  238. //! \brief
  239. //! Manually checks the data trigger on pop and calls it we have match.
  240. //! \param it The item to check against
  241. //! \return True if the callable has called.
  242. bool check_trigger_pop_value_ (const Data_t& it) {
  243. bool match;
  244. switch (trigger_.tdata.type) {
  245. default:
  246. case data_match::DISABLED: match = false; break;
  247. case data_match::MATCH_POP: match = (it == trigger_.tdata.value); break;
  248. case data_match::MISMATCH_POP: match = (it != trigger_.tdata.value); break;
  249. }
  250. if (match)
  251. callback_();
  252. return match;
  253. }
  254. //! Wrapper for both triggers at push
  255. bool check_trigger_push_async_ (const Data_t& it) {
  256. switch (mode_) {
  257. default:
  258. case match_mode::SIZE: return check_trigger_size_();
  259. case match_mode::DATA: return check_trigger_push_value_(it);
  260. }
  261. }
  262. //! Wrapper for both triggers at pop
  263. bool check_trigger_pop_async_ (const Data_t& it) {
  264. switch (mode_) {
  265. default:
  266. case match_mode::SIZE: return check_trigger_size_();
  267. case match_mode::DATA: return check_trigger_pop_value_(it);
  268. }
  269. }
  270. //! @}
  271. private:
  272. match_mode mode_{};
  273. trigger trigger_{};
  274. callable_t callback_{};
  275. };
  276. } // namespace utl
  277. #endif /* utl_container_edeque_h__ */