|
- /*!
- * \file cont/edeque.h
- * \brief
- * A deque with event based callables
- *
- * \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
- *
- * <dl class=\"section copyright\"><dt>License</dt><dd>
- * The MIT License (MIT)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * </dd></dl>
- */
-
- #ifndef TBX_CONT_EDEQUE_H_
- #define TBX_CONT_EDEQUE_H_
-
- #include <core/core.h>
- #include <cont/deque.h>
-
- #include <functional>
- #include <utility>
- #include <type_traits>
-
- namespace tbx {
-
- /*!
- * \class edeque
- * \brief
- * A statically allocated deque with size and data matching event based callables.
- *
- * The edeque inherits deque and provide the callable functionality as a wrapper.
- *
- * There are two types of events.
- * - Size based events, which are cleared as soon as they served. These events are checked
- * every time the deque change its size. If the criteria match we call the callable of type
- * \c Fn
- * - Data based events, which are permanently. These events are checked every time an item is
- * pushed or popped from the deque. If the criteria match we call call the callable of type
- * \c Fn
- *
- * \tparam Data_t The char-like queued item type. Usually \c char
- * \tparam N The size of edeque
- * \tparam SemiAtomic True for semi-atomic operation. In that case the \c ring_iterator is also atomic.
- * \tparam Fn The type of Callable
- * \note
- * SemiAtomic means it is safe to access different ends from different threads. For example one thread can
- * push only from front and another can pop from back to implement a queue.
- */
- template <typename Data_t, size_t N, bool SemiAtomic =false, typename Fn = std::function<void()>>
- class edeque : public deque<Data_t, N, SemiAtomic> {
- public:
- // meta-identity types
- using type = edeque<Data_t, N, SemiAtomic, Fn>;
- using base_type = deque<Data_t, N, SemiAtomic>;
- using callable_t = Fn;
- using range_t = typename base_type::range_t;
-
- // STL
- using value_type = typename base_type::value_type;
- using reference = typename base_type::reference;
- using const_reference = typename base_type::const_reference;
- using pointer = typename base_type::pointer;
- using const_pointer = typename base_type::const_pointer;
- using iterator = typename base_type::iterator;
- using const_iterator = typename base_type::const_iterator;
- using reverse_iterator = typename base_type::reverse_iterator;
- using const_reverse_iterator= typename base_type::const_reverse_iterator;
-
- //! \name Public types
- //! @{
- public:
- //! \enum match_mode
- //! The mode of match operation
- enum class match_mode { SIZE, DATA };
- //! \enum size_match
- //! The type of matching for size based match
- enum class size_match { DISABLED =0, EQ, NE, LT, LE, GT, GE };
- //! \enum data_match
- //! The type of matching for data based match
- enum class data_match { DISABLED =0, MATCH_PUSH, MATCH_POP, MISMATCH_PUSH, MISMATCH_POP};
-
- // TODO: trigger mode for one-shot or repeated functionality
- // enum class trigger_mode { ONE_SHOT, REPEATED };
-
- //! \struct size_trigger
- //! Size trigger data type
- struct size_trigger {
- size_match type;
- size_t size;
- };
- //! \struct data_trigger
- //! Data trigger data type
- struct data_trigger {
- data_match type;
- Data_t value;
- };
- //! \union trigger
- //! \brief
- //! A union for the common types.
- //! There is only one mode. Either "size" with \ref size_match type and a size to match,
- //! or "data" with \ref data_match type and a value to match
- union trigger {
- size_trigger tsize;
- data_trigger tdata;
- };
- //! @}
-
- //! \name Constructor / Destructor
- //! @{
- public:
- //! Default constructor
- constexpr edeque () noexcept :
- base_type() { }
-
- //! Size trigger constructor
- constexpr edeque (size_match match, size_t size, callable_t&& fn) :
- base_type(),
- mode_{match_mode::SIZE},
- callback_{std::forward<callable_t>(fn)} {
- trigger_.tsize.type = match;
- trigger_.tsize.size = size;
- }
- //! Data trigger constructor
- constexpr edeque (data_match match, Data_t value, callable_t&& fn) :
- base_type(),
- mode_{match_mode::DATA},
- callback_{std::forward<callable_t>(fn)} {
- trigger_.tdata.type = match;
- trigger_.tdata.value = value;
- }
- //! @}
-
- //! \name Public interface
- //! @{
-
- //! \brief
- //! Manually checks the size trigger and calls it we have match.
- //! \return True if the callable has called.
- bool check_trigger () {
- return check_trigger_size_();
- }
-
- //! \brief
- //! Manually set (or alters) the \c size trigger. This function does not fire the
- //! \ref check_trigger()
- //! \param match The match type
- //! \param size The size for with we check against
- //! \param fn The callable to call on match
- void set_trigger (size_match match, size_t size, callable_t&& fn) {
- mode_ = match_mode::SIZE;
- trigger_.tsize.type = match;
- trigger_.tsize.size = size;
- callback_ = std::forward<callable_t>(fn);
- }
-
- //! \brief
- //! Manually set (or alters) the \c data trigger. This function does not fire the
- //! \ref check_trigger()
- //! \param match The match type
- //! \param value The value for with we check against
- //! \param fn The callable to call on match
- void set_trigger (data_match match, Data_t value, callable_t&& fn) {
- mode_ = match_mode::DATA;
- trigger_.tdata.type = match;
- trigger_.tdata.value= value;
- callback_ = std::forward<callable_t>(fn);
- }
-
- //! \brief Manually clears the trigger
- void clear_trigger () {
- mode_ = match_mode{};
- trigger_ = trigger{};
- callback_ = callable_t{};
- }
- //! @}
-
- //! \name Base class overwrites
- //! @{
- void push_front (const Data_t& it) {
- base_type::push_front(it);
- check_trigger_push_async_(it);
- }
- Data_t pop_front () {
- Data_t t = base_type::pop_front();
- check_trigger_pop_async_(t);
- return t;
- }
- void push_back (const Data_t& it) {
- base_type::push_back(it);
- check_trigger_push_async_(it);
- }
- Data_t pop_back () {
- Data_t t = base_type::pop_back();
- check_trigger_pop_async_(t);
- return t;
- }
- //! @}
-
- //! \name Private functionality
- //! @{
- private:
- //! \brief
- //! Manually checks the size trigger and calls it we have match.
- //! \return True if the callable has called.
- bool check_trigger_size_ () {
- bool match;
- switch (trigger_.tsize.type) {
- default:
- case size_match::DISABLED: match = false; break;
- case size_match::EQ: match = (base_type::size() == trigger_.tsize.size); break;
- case size_match::NE: match = (base_type::size() != trigger_.tsize.size); break;
- case size_match::LT: match = (base_type::size() < trigger_.tsize.size); break;
- case size_match::LE: match = (base_type::size() <= trigger_.tsize.size); break;
- case size_match::GT: match = (base_type::size() > trigger_.tsize.size); break;
- case size_match::GE: match = (base_type::size() >= trigger_.tsize.size); break;
- }
- if (match) {
- callback_();
- clear_trigger();
- }
- return match;
- }
-
- //! \brief
- //! Manually checks the data trigger on push and calls it we have match.
- //! \param it The item to check against
- //! \return True if the callable has called.
- bool check_trigger_push_value_ (const Data_t& it) {
- bool match;
- switch (trigger_.tdata.type) {
- default:
- case data_match::DISABLED: match = false; break;
- case data_match::MATCH_PUSH: match = (it == trigger_.tdata.value); break;
- case data_match::MISMATCH_PUSH: match = (it != trigger_.tdata.value); break;
- }
- if (match)
- callback_();
- return match;
- }
-
- //! \brief
- //! Manually checks the data trigger on pop and calls it we have match.
- //! \param it The item to check against
- //! \return True if the callable has called.
- bool check_trigger_pop_value_ (const Data_t& it) {
- bool match;
- switch (trigger_.tdata.type) {
- default:
- case data_match::DISABLED: match = false; break;
- case data_match::MATCH_POP: match = (it == trigger_.tdata.value); break;
- case data_match::MISMATCH_POP: match = (it != trigger_.tdata.value); break;
- }
- if (match)
- callback_();
- return match;
- }
-
- //! Wrapper for both triggers at push
- bool check_trigger_push_async_ (const Data_t& it) {
- switch (mode_) {
- default:
- case match_mode::SIZE: return check_trigger_size_();
- case match_mode::DATA: return check_trigger_push_value_(it);
- }
- }
- //! Wrapper for both triggers at pop
- bool check_trigger_pop_async_ (const Data_t& it) {
- switch (mode_) {
- default:
- case match_mode::SIZE: return check_trigger_size_();
- case match_mode::DATA: return check_trigger_pop_value_(it);
- }
- }
- //! @}
- private:
- match_mode mode_{};
- trigger trigger_{};
- callable_t callback_{};
- };
- }
-
- #endif /* TBX_CONT_EDEQUE_H_ */
|