A C++ toolbox repo until the pair uTL/dTL arives
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.
 
 
 

300 lines
12 KiB

  1. /*!
  2. * \file cont/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 TBX_CONT_EDEQUE_H_
  31. #define TBX_CONT_EDEQUE_H_
  32. #include <core/core.h>
  33. #include <cont/deque.h>
  34. #include <functional>
  35. #include <utility>
  36. #include <type_traits>
  37. namespace tbx {
  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. }
  277. #endif /* TBX_CONT_EDEQUE_H_ */