diff --git a/README.md b/README.md index c199268c..869455a0 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Its design goals include: - Offering APIs that closely resemble those of the STL, enabling familiar and consistent usage. -- Maintaining compatibility with C++98 while implementing many features introduced in later standards +- Maintaining compatibility with C++98 while implementing many features introduced in later standards (C++11/14/17/20/23) where possible. - Ensuring deterministic behavior, which is critical in real-time and resource-constrained environments. @@ -228,7 +228,7 @@ extern "C" etl::chrono::high_resolution_clock::rep etl_get_high_resolution_clock() { - etl::chrono::high_resolution_clock::rep(static_cast(getSystemTimeNs())); + return etl::chrono::high_resolution_clock::rep(static_cast(getSystemTimeNs())); } etl::chrono::system_clock::rep etl_get_system_clock() diff --git a/include/etl/generators/message_router_generator.h b/include/etl/generators/message_router_generator.h index 02140088..eeec698f 100644 --- a/include/etl/generators/message_router_generator.h +++ b/include/etl/generators/message_router_generator.h @@ -77,6 +77,8 @@ cog.outl("//******************************************************************** #include "successor.h" #include "type_traits.h" #include "type_list.h" +#include "array.h" + #include namespace etl @@ -107,6 +109,53 @@ namespace etl } }; + namespace private_message_router + { + //*************************************************************************** + // Traits for a message router. + // message packet type + // message type_list + // sorted message type_list. + //*************************************************************************** + template + class traits + { +#if ETL_USING_CPP11 + private: + + using message_id_sequence = etl::index_sequence; + + public: + + using message_packet = etl::message_packet; + using message_types = etl::type_list; + using sorted_message_types = etl::type_list_sort_t; + + static_assert(etl::type_list_is_unique::value, "All TMessageTypes must be unique"); + static_assert(etl::type_list_all_of::value, "All TMessageTypes must satisfy the condition etl::is_message_type"); + static_assert(etl::index_sequence_is_unique::value, "All message IDs must be unique"); +#endif + }; + + //*************************************************************************** + // Specialisation of traits for no message types. + // message packet type + // message type_list + // sorted message type_list. + //*************************************************************************** + template <> + class traits<> + { + public: + +#if ETL_USING_CPP11 + using message_packet = etl::message_packet<>; + using message_types = etl::type_list<>; + using sorted_message_types = etl::type_list<>; +#endif + }; + } + //*************************************************************************** /// Forward declare null message router functionality. //*************************************************************************** @@ -200,6 +249,7 @@ namespace etl /// This router can be used as a sink for messages or a 'null source' router. //*************************************************************************** class null_message_router : public imessage_router + , public private_message_router::traits<> { public: @@ -214,7 +264,7 @@ namespace etl : imessage_router(imessage_router::NULL_MESSAGE_ROUTER, successor_) { } - + //******************************************** using etl::imessage_router::receive; @@ -278,6 +328,7 @@ namespace etl /// This router can be used as a producer-only of messages, such an interrupt routine. //*************************************************************************** class message_producer : public imessage_router + , public private_message_router::traits<> { public: @@ -410,32 +461,41 @@ namespace etl destination.receive(id, message); } -//************************************************************************************************* -// For C++17 and above. -//************************************************************************************************* -#if ETL_USING_CPP17 && !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION) + //************************************************************************************************* + // For C++11 and above. + //************************************************************************************************* +#if ETL_USING_CPP11 && !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION) //*************************************************************************** // The definition for all message types. //*************************************************************************** template class message_router : public imessage_router + , public private_message_router::traits { public: - typedef etl::message_packet message_packet; + using typename private_message_router::traits::message_packet; + using typename private_message_router::traits::message_types; + using typename private_message_router::traits::sorted_message_types; + //********************************************** + /// Default constructor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router() : imessage_router(etl::imessage_router::MESSAGE_ROUTER) { } + //********************************************** + /// Constructor with successor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router(etl::imessage_router& successor_) : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) { } + //********************************************** + /// Constructor with message router id. //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -443,6 +503,8 @@ namespace etl ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); } + //********************************************** + /// Constructor with message router id and successor. //********************************************** message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) : imessage_router(id_, successor_) @@ -450,15 +512,36 @@ namespace etl ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); } + //********************************************** + /// Allow visibility of base class receive() methods. //********************************************** using etl::imessage_router::receive; + //********************************************** + /// This will be called for all messages passed as an etl::imessage. + /// It will dispatch the message to the correct handler based on the message id, + /// or pass it to a successor if there is no handler for the message id. + /// \param msg The message. + //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - const bool was_handled = (receive_message_type(msg) || ...); + etl::message_id_t id = msg.get_message_id(); + size_t index = Number_Of_Messages; - if (!was_handled) + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) { + index = get_dispatch_index_from_message_id(id); + } + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + dispatch(msg, index); + } + else + { + // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. if (has_successor()) { get_successor().receive(msg); @@ -472,34 +555,75 @@ namespace etl } } - template ::value, int>::type = 0> + //********************************************** + /// This will be called for messages where TMessage is in the message type list. + /// \tparam TMessage The message type. + /// \param msg The message. + /// Enabled if TMessage is in the message type list. + //********************************************** + template ::value, int>::type = 0> void receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" - if constexpr (etl::is_one_of::value) + static_cast(this)->on_receive(msg); +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + /// This will be called for messages where TMessage is a message type, but not in the message type list. + /// \tparam TMessage The message type. + /// \param msg The message. + /// Enabled if TMessage is a message type, but not in the message type list. + //********************************************** + template ::value && + !etl::is_one_of::value, int>::type = 0> + void receive(const TMessage& msg) + { + if (has_successor()) { - static_cast(this)->on_receive(msg); + get_successor().receive(msg); + } + else + { +#include "etl/private/diagnostic_array_bounds_push.h" + static_cast(this)->on_receive_unknown(msg); +#include "etl/private/diagnostic_pop.h" + } + } + + //********************************************** + /// Allow visibility of base class accepts() methods. + //********************************************** + using imessage_router::accepts; + + //********************************************** + /// This will return true if the message id is in the message type list, or if a successor accepts the message id. + //*********************************************** + bool accepts(etl::message_id_t id) const ETL_OVERRIDE + { + size_t index = Number_Of_Messages; + + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) + { + index = get_dispatch_index_from_message_id(id); + } + + if (index < Number_Of_Messages) + { + return true; } else { if (has_successor()) { - get_successor().receive(msg); + return get_successor().accepts(id); } else { - static_cast(this)->on_receive_unknown(msg); + return false; } } -#include "etl/private/diagnostic_pop.h" - } - - //********************************************** - using imessage_router::accepts; - - bool accepts(etl::message_id_t id) const ETL_OVERRIDE - { - return (accepts_type(id) || ...); } //******************************************** @@ -522,68 +646,183 @@ namespace etl private: - //******************************************** - template - bool receive_message_type(const etl::imessage& msg) + static constexpr size_t Number_Of_Messages = sizeof...(TMessageTypes); + static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t::ID; + + //********************************************** + // Checks that the message ids are contiguous. + //********************************************** + template = Number_Of_Messages)> + struct contiguous_impl; + + template + struct contiguous_impl : etl::true_type { - if (TMessage::ID == msg.get_message_id()) - { -#include "etl/private/diagnostic_array_bounds_push.h" - static_cast(this)->on_receive(static_cast(msg)); -#include "etl/private/diagnostic_pop.h" - return true; - } - else - { - return false; - } + }; + + template + struct contiguous_impl + : etl::bool_constant<(etl::type_list_type_at_index_t::ID + 1U == + etl::type_list_type_at_index_t::ID) && + contiguous_impl::value> + { + }; + + // The message ids are contiguous if there are 0 or 1 message types, or if each message id is one greater than the previous message id. + static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value; + + using handler_ptr = void (*)(TDerived&, const etl::imessage&); ///< Pointer to a handler function that takes a reference to the derived class and a reference to the message. + using message_dispatch_table_t = etl::array; ///< The dispatch table type. An array of handler pointers, one for each message type. + using message_id_table_t = etl::array; ///< The message id table type. An array of message ids, one for each message type. + + //********************************************** + // Call for a single message type + //********************************************** + template + static void call_on_receive(TDerived& derived, const imessage& msg) + { + derived.on_receive(static_cast(msg)); } - //******************************************** - template - bool accepts_type(etl::message_id_t id) const + //********************************************** + // Get the handler for a single message type at the index in the sorted type_list. + // This will be called for each message type to generate the dispatch table. + //********************************************** + template + static constexpr handler_ptr get_message_handler() { - if (TMessage::ID == id) + return &call_on_receive>; + } + + //********************************************** + // Generate the dispatch table at compile time. + // This will create an array of handler pointers, one for each message type. + //********************************************** + template + static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence) + { + return message_dispatch_table_t{ { get_message_handler()... } }; + } + + //********************************************** + // Get the message id for a single message type at an index in the sorted type_list. + // This will be called for each message type to generate the message id table. + //********************************************** + template + static constexpr etl::message_id_t get_message_id_from_index() + { + return etl::type_list_type_at_index_t::ID; + } + + //********************************************** + // Generate the message id table at compile time. + // This will create an array of message ids, one for each message type. + //********************************************** + template + static constexpr message_id_table_t make_message_id_table(etl::index_sequence) + { + return message_id_table_t{ { get_message_id_from_index()... } }; + } + + //********************************************** + // Get the dispatch index for a message id. + // This will be used at runtime to find the handler for a message id. + // If the message ids are contiguous, we can calculate the index directly. If they are not contiguous, we need to do a binary search. + // This will return Number_Of_Messages if the message id is not found, which indicates that the message should be passed to the successor. + //********************************************** + static size_t get_dispatch_index_from_message_id(etl::message_id_t id) + { + if ETL_IF_CONSTEXPR(Message_Ids_Are_Contiguous) { - return true; + // The IDs are contiguous, so we can calculate the index directly. + return static_cast(id - Message_Id_Start); } else { - if (has_successor()) + // The IDs are not contiguous, so we need to do a binary search. + size_t left = 0; + size_t right = Number_Of_Messages; + + while (left < right) { - return get_successor().accepts(id); - } - else - { - return false; + size_t mid = (left + right) / 2; + + if (message_id_table[mid] == id) + { + return mid; + } + else if (message_id_table[mid] < id) + { + left = mid + 1; + } + else + { + right = mid; + } } } + + return Number_Of_Messages; // Not found } + + //********************************************** + // Dispatch the message to the appropriate handler based on the index in the dispatch table. + //********************************************** + void dispatch(const etl::imessage& msg, size_t index) + { + message_dispatch_table[index](static_cast(*this), msg); + } + + //********************************************** + // The dispatch table is generated at compile time. The dispatch table contains pointers to the on_receive handlers for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_dispatch_table_t message_dispatch_table = + etl::message_router::make_message_dispatch_table(etl::make_index_sequence::Number_Of_Messages>{}); + + //********************************************** + // The message id table is generated at compile time. The message id table contains the corresponding message ids for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_id_table_t message_id_table = + etl::message_router::make_message_id_table(etl::make_index_sequence::Number_Of_Messages>{}); }; +#if ETL_USING_CPP11 && !ETL_USING_CPP17 + template + constexpr const typename etl::message_router::message_dispatch_table_t + etl::message_router::message_dispatch_table; + + template + constexpr const typename etl::message_router::message_id_table_t + etl::message_router::message_id_table; +#endif + //*************************************************************************** - // The definition for 0 message types. + // The definition of a message_router for zero message types. //*************************************************************************** template class message_router : public imessage_router + , public private_message_router::traits<> { public: - using message_packet = etl::message_packet<>; - using message_types = etl::type_list<>; - + //********************************************** + /// Default constructor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router() : imessage_router(etl::imessage_router::MESSAGE_ROUTER) { } + //********************************************** + /// Constructor with successor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router(etl::imessage_router& successor_) : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) { } + //********************************************** + /// Constructor with message router id. //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -591,6 +830,8 @@ namespace etl ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); } + //********************************************** + /// Constructor with message router id and successor. //********************************************** message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) : imessage_router(id_, successor_) @@ -599,19 +840,16 @@ namespace etl } //********************************************** + /// Allow visibility of base class receive() methods. using etl::imessage_router::receive; + //********************************************** + /// This will be called for all messages passed as an etl::imessage. + /// Since there are no message types, this will just pass the message to a successor if there is one, or call on_receive_unknown() if there isn't. + /// \param msg The message. + //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - if (has_successor()) - { - get_successor().receive(msg); - } - } - - template ::value, int>::type = 0> - void receive(const TMessage& msg) - { #include "etl/private/diagnostic_array_bounds_push.h" if (has_successor()) { @@ -620,9 +858,14 @@ namespace etl #include "etl/private/diagnostic_pop.h" } + //********************************************** + /// Allow visibility of base class accepts() methods. //********************************************** using imessage_router::accepts; + //********************************************** + /// This will return true if a successor accepts the message id. + //*********************************************** bool accepts(etl::message_id_t id) const ETL_OVERRIDE { if (has_successor()) @@ -656,6 +899,7 @@ namespace etl //*************************************************************************** /// Helper to turn etl::type_list into etl::message_router + //*************************************************************************** template struct message_router_from_type_list; @@ -669,9 +913,9 @@ namespace etl using message_router_from_type_list_t = typename message_router_from_type_list::type; #else -//************************************************************************************************* -// For C++14 and below. -//************************************************************************************************* + //************************************************************************************************* + // For C++03/98. + //************************************************************************************************* /*[[[cog import cog ################################################ @@ -683,10 +927,12 @@ namespace etl cog.outl("template " % int(Handlers)) cog.out("class message_router") cog.outl(" : public imessage_router") @@ -698,8 +944,8 @@ namespace etl cog.out("T%s, " % n) cog.outl("T%s> message_packet;" % int(Handlers)) cog.outl("") - - cog.outl("#if ETL_USING_CPP11") + + cog.outl("#if ETL_USING_CPP11") cog.out(" using message_types = etl::type_list<") for n in range(1, int(Handlers)): cog.out("T%s, " % n) @@ -764,7 +1010,7 @@ namespace etl cog.outl(" }") cog.outl("") cog.outl(" template ") - cog.out(" typename etl::enable_if::value && etl::is_one_of::value && etl::is_one_of::value, void>::type" % int(Handlers)) @@ -776,7 +1022,7 @@ namespace etl cog.outl(" }") cog.outl("") cog.outl(" template ") - cog.out(" typename etl::enable_if::value && !etl::is_one_of::value && !etl::is_one_of::value, void>::type" % int(Handlers)) @@ -801,13 +1047,13 @@ namespace etl cog.outl(" {") cog.outl(" switch (id)") cog.outl(" {") - cog.out(" ") + cog.out(" ") for n in range(1, int(Handlers) + 1): - cog.out("case T%d::ID: " % n) + cog.out(" case T%d::ID:" % n) if n % 8 == 0: cog.outl("") - cog.out(" ") - cog.outl(" return true;") + cog.out(" ") + cog.outl(" return true;") cog.outl(" default:") cog.outl(" {") cog.outl(" if (has_successor())") @@ -852,13 +1098,15 @@ namespace etl else: cog.outl("// Specialisation for %d message types." % n) cog.outl("//***************************************************************************") - cog.outl("template " % n) cog.out("class message_router message_packet;" % n) cog.outl("") - cog.outl("#if ETL_USING_CPP11") + cog.outl("#if ETL_USING_CPP11") cog.out(" using message_types = etl::type_list<") for t in range(1, n): cog.out("T%s, " % t) @@ -947,7 +1195,7 @@ namespace etl cog.outl(" }") cog.outl("") cog.outl(" template ") - cog.out(" typename etl::enable_if::value && etl::is_one_of::value && etl::is_one_of::value, void>::type" % n) @@ -959,7 +1207,7 @@ namespace etl cog.outl(" }") cog.outl("") cog.outl(" template ") - cog.out(" typename etl::enable_if::value && !etl::is_one_of::value && !etl::is_one_of::value, void>::type" % n) @@ -985,12 +1233,13 @@ namespace etl cog.outl(" {") cog.outl(" switch (id)") cog.outl(" {") - cog.out(" ") + cog.out(" ") for t in range(1, n + 1): - cog.out("case T%d::ID: " % t) + cog.out(" case T%d::ID:" % t) if t % 8 == 0: cog.outl("") - cog.out(" ") + if t != n: + cog.out(" ") cog.outl("") cog.outl(" return true;") cog.outl(" default:") diff --git a/include/etl/message.h b/include/etl/message.h index 9c664abc..4811747a 100644 --- a/include/etl/message.h +++ b/include/etl/message.h @@ -263,6 +263,106 @@ namespace etl template inline constexpr bool is_user_message_base_v = is_user_message_base::value; #endif + + //*************************************************************************** + /// Detects presence of a static ID member. + //*************************************************************************** + template + struct has_message_id + { + private: + + ETL_STATIC_ASSERT(etl::is_message::value, "T is not an ETL message"); + + typedef char yes; + struct no { char value[2]; }; + + template + static yes test(char (*)[sizeof(&U::ID)]); + + template + static no test(...); + + public: + + static const bool value = sizeof(test::type>(0)) == sizeof(yes); + }; + + template + const bool has_message_id::value; + +#if ETL_USING_CPP17 + template + inline constexpr bool has_message_id_v = has_message_id::value; +#endif + +#if ETL_USING_CPP11 + namespace private_message + { + template + struct compare_message_id_less_impl; + + //********************************************** + // Compare the message ID of two messages. + // \tparam TMsg1 The first message type. + // \tparam TMsg2 The second message type. + // Only selected if both TMsg1 or TMsg2 don't have an ID. + //********************************************** + template + struct compare_message_id_less_impl + { + ETL_STATIC_ASSERT(etl::has_message_id::value, "TMsg1 does not have an ID"); + ETL_STATIC_ASSERT(etl::has_message_id::value, "TMsg2 does not have an ID"); + ETL_STATIC_ASSERT(etl::is_message_type::value, "TMsg1 is not derived from etl::message<>"); + ETL_STATIC_ASSERT(etl::is_message_type::value, "TMsg2 is not derived from etl::message<>"); + }; + + //********************************************** + // Compare the message ID of two messages. + // \tparam TMsg1 The first message type. + // \tparam TMsg2 The second message type. + // value is true if TMsg1::ID < TMsg2::ID. + // Only selected if both TMsg1 and TMsg2 have an ID. + //*********************************************** + template + struct compare_message_id_less_impl + : etl::bool_constant < TMsg1::ID < TMsg2::ID> + { + ETL_STATIC_ASSERT(etl::is_message_type::value, "TMsg1 is not derived from etl::message<>"); + ETL_STATIC_ASSERT(etl::is_message_type::value, "TMsg2 is not derived from etl::message<>"); + }; + } + + //********************************************** + /// Compare the message ID of two messages. + /// \tparam TMsg1 The first message type. + /// \tparam TMsg2 The second message type. + /// value is true if TMsg1::ID < TMsg2::ID. + //********************************************** + template + struct compare_message_id_less : public private_message::compare_message_id_less_impl::value && has_message_id::value> + { + }; +#else + //********************************************** + /// Compare the message ID of two messages. + /// \tparam TMsg1 The first message type. + /// \tparam TMsg2 The second message type. + /// value is true if TMsg1::ID < TMsg2::ID. + //********************************************** + template + struct compare_message_id_less + : etl::bool_constant + { + ETL_STATIC_ASSERT(etl::is_message_type::value, "TMsg1 is not derived from etl::message<>"); + ETL_STATIC_ASSERT(etl::is_message_type::value, "TMsg2 is not derived from etl::message<>"); + }; +#endif + +#if ETL_USING_CPP17 + template + inline constexpr bool compare_message_id_less_v = compare_message_id_less::value; +#endif } #endif diff --git a/include/etl/message_router.h b/include/etl/message_router.h index b53d9071..e9b79d30 100644 --- a/include/etl/message_router.h +++ b/include/etl/message_router.h @@ -65,6 +65,8 @@ SOFTWARE. #include "successor.h" #include "type_traits.h" #include "type_list.h" +#include "array.h" + #include namespace etl @@ -95,6 +97,53 @@ namespace etl } }; + namespace private_message_router + { + //*************************************************************************** + // Traits for a message router. + // message packet type + // message type_list + // sorted message type_list. + //*************************************************************************** + template + class traits + { +#if ETL_USING_CPP11 + private: + + using message_id_sequence = etl::index_sequence; + + public: + + using message_packet = etl::message_packet; + using message_types = etl::type_list; + using sorted_message_types = etl::type_list_sort_t; + + static_assert(etl::type_list_is_unique::value, "All TMessageTypes must be unique"); + static_assert(etl::type_list_all_of::value, "All TMessageTypes must satisfy the condition etl::is_message_type"); + static_assert(etl::index_sequence_is_unique::value, "All message IDs must be unique"); +#endif + }; + + //*************************************************************************** + // Specialisation of traits for no message types. + // message packet type + // message type_list + // sorted message type_list. + //*************************************************************************** + template <> + class traits<> + { + public: + +#if ETL_USING_CPP11 + using message_packet = etl::message_packet<>; + using message_types = etl::type_list<>; + using sorted_message_types = etl::type_list<>; +#endif + }; + } + //*************************************************************************** /// Forward declare null message router functionality. //*************************************************************************** @@ -188,6 +237,7 @@ namespace etl /// This router can be used as a sink for messages or a 'null source' router. //*************************************************************************** class null_message_router : public imessage_router + , public private_message_router::traits<> { public: @@ -202,7 +252,7 @@ namespace etl : imessage_router(imessage_router::NULL_MESSAGE_ROUTER, successor_) { } - + //******************************************** using etl::imessage_router::receive; @@ -266,6 +316,7 @@ namespace etl /// This router can be used as a producer-only of messages, such an interrupt routine. //*************************************************************************** class message_producer : public imessage_router + , public private_message_router::traits<> { public: @@ -398,32 +449,41 @@ namespace etl destination.receive(id, message); } -//************************************************************************************************* -// For C++17 and above. -//************************************************************************************************* -#if ETL_USING_CPP17 && !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION) + //************************************************************************************************* + // For C++11 and above. + //************************************************************************************************* +#if ETL_USING_CPP11 && !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION) //*************************************************************************** // The definition for all message types. //*************************************************************************** template class message_router : public imessage_router + , public private_message_router::traits { public: - typedef etl::message_packet message_packet; + using typename private_message_router::traits::message_packet; + using typename private_message_router::traits::message_types; + using typename private_message_router::traits::sorted_message_types; + //********************************************** + /// Default constructor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router() : imessage_router(etl::imessage_router::MESSAGE_ROUTER) { } + //********************************************** + /// Constructor with successor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router(etl::imessage_router& successor_) : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) { } + //********************************************** + /// Constructor with message router id. //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -431,6 +491,8 @@ namespace etl ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); } + //********************************************** + /// Constructor with message router id and successor. //********************************************** message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) : imessage_router(id_, successor_) @@ -438,15 +500,36 @@ namespace etl ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); } + //********************************************** + /// Allow visibility of base class receive() methods. //********************************************** using etl::imessage_router::receive; + //********************************************** + /// This will be called for all messages passed as an etl::imessage. + /// It will dispatch the message to the correct handler based on the message id, + /// or pass it to a successor if there is no handler for the message id. + /// \param msg The message. + //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - const bool was_handled = (receive_message_type(msg) || ...); + etl::message_id_t id = msg.get_message_id(); + size_t index = Number_Of_Messages; - if (!was_handled) + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) { + index = get_dispatch_index_from_message_id(id); + } + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + dispatch(msg, index); + } + else + { + // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. if (has_successor()) { get_successor().receive(msg); @@ -460,34 +543,75 @@ namespace etl } } - template ::value, int>::type = 0> + //********************************************** + /// This will be called for messages where TMessage is in the message type list. + /// \tparam TMessage The message type. + /// \param msg The message. + /// Enabled if TMessage is in the message type list. + //********************************************** + template ::value, int>::type = 0> void receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" - if constexpr (etl::is_one_of::value) + static_cast(this)->on_receive(msg); +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + /// This will be called for messages where TMessage is a message type, but not in the message type list. + /// \tparam TMessage The message type. + /// \param msg The message. + /// Enabled if TMessage is a message type, but not in the message type list. + //********************************************** + template ::value && + !etl::is_one_of::value, int>::type = 0> + void receive(const TMessage& msg) + { + if (has_successor()) { - static_cast(this)->on_receive(msg); + get_successor().receive(msg); + } + else + { +#include "etl/private/diagnostic_array_bounds_push.h" + static_cast(this)->on_receive_unknown(msg); +#include "etl/private/diagnostic_pop.h" + } + } + + //********************************************** + /// Allow visibility of base class accepts() methods. + //********************************************** + using imessage_router::accepts; + + //********************************************** + /// This will return true if the message id is in the message type list, or if a successor accepts the message id. + //*********************************************** + bool accepts(etl::message_id_t id) const ETL_OVERRIDE + { + size_t index = Number_Of_Messages; + + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) + { + index = get_dispatch_index_from_message_id(id); + } + + if (index < Number_Of_Messages) + { + return true; } else { if (has_successor()) { - get_successor().receive(msg); + return get_successor().accepts(id); } else { - static_cast(this)->on_receive_unknown(msg); + return false; } } -#include "etl/private/diagnostic_pop.h" - } - - //********************************************** - using imessage_router::accepts; - - bool accepts(etl::message_id_t id) const ETL_OVERRIDE - { - return (accepts_type(id) || ...); } //******************************************** @@ -510,68 +634,183 @@ namespace etl private: - //******************************************** - template - bool receive_message_type(const etl::imessage& msg) + static constexpr size_t Number_Of_Messages = sizeof...(TMessageTypes); + static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t::ID; + + //********************************************** + // Checks that the message ids are contiguous. + //********************************************** + template = Number_Of_Messages)> + struct contiguous_impl; + + template + struct contiguous_impl : etl::true_type { - if (TMessage::ID == msg.get_message_id()) - { -#include "etl/private/diagnostic_array_bounds_push.h" - static_cast(this)->on_receive(static_cast(msg)); -#include "etl/private/diagnostic_pop.h" - return true; - } - else - { - return false; - } + }; + + template + struct contiguous_impl + : etl::bool_constant<(etl::type_list_type_at_index_t::ID + 1U == + etl::type_list_type_at_index_t::ID) && + contiguous_impl::value> + { + }; + + // The message ids are contiguous if there are 0 or 1 message types, or if each message id is one greater than the previous message id. + static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value; + + using handler_ptr = void (*)(TDerived&, const etl::imessage&); ///< Pointer to a handler function that takes a reference to the derived class and a reference to the message. + using message_dispatch_table_t = etl::array; ///< The dispatch table type. An array of handler pointers, one for each message type. + using message_id_table_t = etl::array; ///< The message id table type. An array of message ids, one for each message type. + + //********************************************** + // Call for a single message type + //********************************************** + template + static void call_on_receive(TDerived& derived, const imessage& msg) + { + derived.on_receive(static_cast(msg)); } - //******************************************** - template - bool accepts_type(etl::message_id_t id) const + //********************************************** + // Get the handler for a single message type at the index in the sorted type_list. + // This will be called for each message type to generate the dispatch table. + //********************************************** + template + static constexpr handler_ptr get_message_handler() { - if (TMessage::ID == id) + return &call_on_receive>; + } + + //********************************************** + // Generate the dispatch table at compile time. + // This will create an array of handler pointers, one for each message type. + //********************************************** + template + static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence) + { + return message_dispatch_table_t{ { get_message_handler()... } }; + } + + //********************************************** + // Get the message id for a single message type at an index in the sorted type_list. + // This will be called for each message type to generate the message id table. + //********************************************** + template + static constexpr etl::message_id_t get_message_id_from_index() + { + return etl::type_list_type_at_index_t::ID; + } + + //********************************************** + // Generate the message id table at compile time. + // This will create an array of message ids, one for each message type. + //********************************************** + template + static constexpr message_id_table_t make_message_id_table(etl::index_sequence) + { + return message_id_table_t{ { get_message_id_from_index()... } }; + } + + //********************************************** + // Get the dispatch index for a message id. + // This will be used at runtime to find the handler for a message id. + // If the message ids are contiguous, we can calculate the index directly. If they are not contiguous, we need to do a binary search. + // This will return Number_Of_Messages if the message id is not found, which indicates that the message should be passed to the successor. + //********************************************** + static size_t get_dispatch_index_from_message_id(etl::message_id_t id) + { + if ETL_IF_CONSTEXPR(Message_Ids_Are_Contiguous) { - return true; + // The IDs are contiguous, so we can calculate the index directly. + return static_cast(id - Message_Id_Start); } else { - if (has_successor()) + // The IDs are not contiguous, so we need to do a binary search. + size_t left = 0; + size_t right = Number_Of_Messages; + + while (left < right) { - return get_successor().accepts(id); - } - else - { - return false; + size_t mid = (left + right) / 2; + + if (message_id_table[mid] == id) + { + return mid; + } + else if (message_id_table[mid] < id) + { + left = mid + 1; + } + else + { + right = mid; + } } } + + return Number_Of_Messages; // Not found } + + //********************************************** + // Dispatch the message to the appropriate handler based on the index in the dispatch table. + //********************************************** + void dispatch(const etl::imessage& msg, size_t index) + { + message_dispatch_table[index](static_cast(*this), msg); + } + + //********************************************** + // The dispatch table is generated at compile time. The dispatch table contains pointers to the on_receive handlers for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_dispatch_table_t message_dispatch_table = + etl::message_router::make_message_dispatch_table(etl::make_index_sequence::Number_Of_Messages>{}); + + //********************************************** + // The message id table is generated at compile time. The message id table contains the corresponding message ids for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_id_table_t message_id_table = + etl::message_router::make_message_id_table(etl::make_index_sequence::Number_Of_Messages>{}); }; +#if ETL_USING_CPP11 && !ETL_USING_CPP17 + template + constexpr const typename etl::message_router::message_dispatch_table_t + etl::message_router::message_dispatch_table; + + template + constexpr const typename etl::message_router::message_id_table_t + etl::message_router::message_id_table; +#endif + //*************************************************************************** - // The definition for 0 message types. + // The definition of a message_router for zero message types. //*************************************************************************** template class message_router : public imessage_router + , public private_message_router::traits<> { public: - using message_packet = etl::message_packet<>; - using message_types = etl::type_list<>; - + //********************************************** + /// Default constructor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router() : imessage_router(etl::imessage_router::MESSAGE_ROUTER) { } + //********************************************** + /// Constructor with successor. The message router id will be MESSAGE_ROUTER. //********************************************** message_router(etl::imessage_router& successor_) : imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_) { } + //********************************************** + /// Constructor with message router id. //********************************************** message_router(etl::message_router_id_t id_) : imessage_router(id_) @@ -579,6 +818,8 @@ namespace etl ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id)); } + //********************************************** + /// Constructor with message router id and successor. //********************************************** message_router(etl::message_router_id_t id_, etl::imessage_router& successor_) : imessage_router(id_, successor_) @@ -587,19 +828,16 @@ namespace etl } //********************************************** + /// Allow visibility of base class receive() methods. using etl::imessage_router::receive; + //********************************************** + /// This will be called for all messages passed as an etl::imessage. + /// Since there are no message types, this will just pass the message to a successor if there is one, or call on_receive_unknown() if there isn't. + /// \param msg The message. + //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - if (has_successor()) - { - get_successor().receive(msg); - } - } - - template ::value, int>::type = 0> - void receive(const TMessage& msg) - { #include "etl/private/diagnostic_array_bounds_push.h" if (has_successor()) { @@ -608,9 +846,14 @@ namespace etl #include "etl/private/diagnostic_pop.h" } + //********************************************** + /// Allow visibility of base class accepts() methods. //********************************************** using imessage_router::accepts; + //********************************************** + /// This will return true if a successor accepts the message id. + //*********************************************** bool accepts(etl::message_id_t id) const ETL_OVERRIDE { if (has_successor()) @@ -644,6 +887,7 @@ namespace etl //*************************************************************************** /// Helper to turn etl::type_list into etl::message_router + //*************************************************************************** template struct message_router_from_type_list; @@ -657,16 +901,16 @@ namespace etl using message_router_from_type_list_t = typename message_router_from_type_list::type; #else -//************************************************************************************************* -// For C++14 and below. -//************************************************************************************************* + //************************************************************************************************* + // For C++03/98. + //************************************************************************************************* //*************************************************************************** // The definition for all 16 message types. //*************************************************************************** template class message_router : public imessage_router { @@ -747,7 +991,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -756,7 +1000,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -778,8 +1022,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: case T14::ID: case T15::ID: case T16::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: case T14::ID: case T15::ID: case T16::ID: return true; default: { @@ -817,10 +1061,10 @@ namespace etl //*************************************************************************** // Specialisation for 15 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -901,7 +1145,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -910,7 +1154,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -933,8 +1177,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: case T14::ID: case T15::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: case T14::ID: case T15::ID: return true; default: { @@ -972,10 +1216,10 @@ namespace etl //*************************************************************************** // Specialisation for 14 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -1055,7 +1299,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -1064,7 +1308,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -1087,8 +1331,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: case T14::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: case T14::ID: return true; default: { @@ -1126,10 +1370,10 @@ namespace etl //*************************************************************************** // Specialisation for 13 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -1208,7 +1452,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -1217,7 +1461,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -1240,8 +1484,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: case T10::ID: case T11::ID: case T12::ID: case T13::ID: return true; default: { @@ -1279,9 +1523,9 @@ namespace etl //*************************************************************************** // Specialisation for 12 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -1359,7 +1603,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -1368,7 +1612,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -1391,8 +1635,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: case T10::ID: case T11::ID: case T12::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: case T10::ID: case T11::ID: case T12::ID: return true; default: { @@ -1430,9 +1674,9 @@ namespace etl //*************************************************************************** // Specialisation for 11 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -1509,7 +1753,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -1518,7 +1762,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -1541,8 +1785,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: case T10::ID: case T11::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: case T10::ID: case T11::ID: return true; default: { @@ -1580,9 +1824,9 @@ namespace etl //*************************************************************************** // Specialisation for 10 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -1658,7 +1902,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -1667,7 +1911,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -1690,8 +1934,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: case T10::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: case T10::ID: return true; default: { @@ -1729,9 +1973,9 @@ namespace etl //*************************************************************************** // Specialisation for 9 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -1806,7 +2050,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -1815,7 +2059,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -1838,8 +2082,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - case T9::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + case T9::ID: return true; default: { @@ -1877,8 +2121,8 @@ namespace etl //*************************************************************************** // Specialisation for 8 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -1952,7 +2196,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -1961,7 +2205,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -1984,8 +2228,8 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: - + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: case T8::ID: + return true; default: { @@ -2023,8 +2267,8 @@ namespace etl //*************************************************************************** // Specialisation for 7 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -2097,7 +2341,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -2106,7 +2350,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -2129,7 +2373,7 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: case T7::ID: return true; default: { @@ -2167,8 +2411,8 @@ namespace etl //*************************************************************************** // Specialisation for 6 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -2240,7 +2484,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -2249,7 +2493,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -2272,7 +2516,7 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: case T6::ID: return true; default: { @@ -2310,8 +2554,8 @@ namespace etl //*************************************************************************** // Specialisation for 5 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -2382,7 +2626,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -2391,7 +2635,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -2414,7 +2658,7 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: case T5::ID: return true; default: { @@ -2452,7 +2696,7 @@ namespace etl //*************************************************************************** // Specialisation for 4 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -2522,7 +2766,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -2531,7 +2775,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -2554,7 +2798,7 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: case T4::ID: + case T1::ID: case T2::ID: case T3::ID: case T4::ID: return true; default: { @@ -2592,7 +2836,7 @@ namespace etl //*************************************************************************** // Specialisation for 3 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -2661,7 +2905,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -2670,7 +2914,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -2693,7 +2937,7 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: case T3::ID: + case T1::ID: case T2::ID: case T3::ID: return true; default: { @@ -2731,7 +2975,7 @@ namespace etl //*************************************************************************** // Specialisation for 2 message types. //*************************************************************************** - template class message_router : public imessage_router @@ -2799,7 +3043,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -2808,7 +3052,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -2831,7 +3075,7 @@ namespace etl { switch (id) { - case T1::ID: case T2::ID: + case T1::ID: case T2::ID: return true; default: { @@ -2869,7 +3113,7 @@ namespace etl //*************************************************************************** // Specialisation for 1 message type. //*************************************************************************** - template class message_router : public imessage_router @@ -2936,7 +3180,7 @@ namespace etl } template - typename etl::enable_if::value && etl::is_one_of::value, void>::type + typename etl::enable_if::value && etl::is_one_of::value, void>::type receive(const TMessage& msg) { #include "etl/private/diagnostic_array_bounds_push.h" @@ -2945,7 +3189,7 @@ namespace etl } template - typename etl::enable_if::value && !etl::is_one_of::value, void>::type + typename etl::enable_if::value && !etl::is_one_of::value, void>::type receive(const TMessage& msg) { if (has_successor()) @@ -2968,7 +3212,7 @@ namespace etl { switch (id) { - case T1::ID: + case T1::ID: return true; default: { diff --git a/include/etl/utility.h b/include/etl/utility.h index 6e997f16..f2419023 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -762,6 +762,112 @@ namespace etl template inline constexpr size_t index_sequence_at_v = index_sequence_at::value; #endif + + //************************************ + /// Checks if an index_sequence contains a value. + //************************************ + template + struct index_sequence_contains; + + // An empty sequence does not contain any value. + template + struct index_sequence_contains, Value> : etl::false_type + { + }; + + // When the front index of the sequence is the value, the sequence contains the value. + // When the front index of the sequence is not the value, recurse with the tail of the sequence. + template + struct index_sequence_contains, Value> + : etl::integral_constant, Value>::value> + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool index_sequence_contains_v = index_sequence_contains::value; +#endif + + //*************************************************************************** + /// Defines a new index_sequence by removing duplicate indexes from a given index_sequence, preserving the first occurrence. + //*************************************************************************** + namespace private_index_sequence + { + template + struct type_index_sequence_impl; + + // When the front index of the sequence is not in the unique sequence, add it to the back of the unique sequence and recurse with the tail of the sequence. + template + struct type_index_sequence_impl, etl::index_sequence> + { + // If the index is already in the unique sequence, do not add it again. Otherwise, add it to the back of the unique sequence. + using type = typename etl::conditional_t, Index>::value, + type_index_sequence_impl, etl::index_sequence>, + type_index_sequence_impl, etl::index_sequence_push_back_t, Index>>>::type; + }; + + // When the sequence is empty, the unique sequence is the result. + template + struct type_index_sequence_impl, etl::index_sequence> + { + using type = etl::index_sequence; + }; + } + + template + struct index_sequence_unique; + + template + struct index_sequence_unique> + { + using type = typename private_index_sequence::type_index_sequence_impl, etl::index_sequence<>>::type; + }; + +#if ETL_USING_CPP11 + template + using index_sequence_unique_t = typename etl::index_sequence_unique::type; +#endif + + //*************************************************************************** + /// Checks that all of the indices in an index_sequence are unique. + //*************************************************************************** + template + struct index_sequence_is_unique; + + template + struct index_sequence_is_unique> + : etl::bool_constant, + etl::index_sequence_unique_t>>::value> + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool index_sequence_is_unique_v = index_sequence_is_unique::type::value; +#endif + + //*************************************************************************** + /// Checks if the index_sequence is empty. + //*************************************************************************** + template + struct index_sequence_is_empty; + + template <> + struct index_sequence_is_empty> + : etl::true_type + { + }; + + template + struct index_sequence_is_empty> + : etl::false_type + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool index_sequence_is_empty_v = index_sequence_is_empty::value; +#endif #endif //*************************************************************************** diff --git a/test/test_message.cpp b/test/test_message.cpp index d51584f0..8ce04481 100644 --- a/test/test_message.cpp +++ b/test/test_message.cpp @@ -37,7 +37,7 @@ SUITE(test_message) { // imessage // ^ - // MessageBase + // MessageBase // ^ // etl::message // ^ @@ -119,7 +119,7 @@ SUITE(test_message) TEST(test_message_base_type) { struct Message1 : public etl::message<1> {}; - + struct MessageBase : public etl::imessage {}; struct Message2 : public etl::message<2, MessageBase> {}; @@ -129,4 +129,40 @@ SUITE(test_message) CHECK_FALSE((std::is_same::value)); CHECK_FALSE((std::is_same::value)); } + + //************************************************************************* + TEST(test_has_message_id) + { + struct Message1 : public etl::message<1> {}; + struct Message2 : public etl::message<2> {}; + struct MessageNoID : public etl::imessage {}; + + CHECK_TRUE(etl::has_message_id::value); + CHECK_TRUE(etl::has_message_id::value); + CHECK_FALSE(etl::has_message_id::value); + +#if ETL_USING_CPP17 + CHECK_TRUE(etl::has_message_id_v); + CHECK_TRUE(etl::has_message_id_v); + CHECK_FALSE(etl::has_message_id_v); +#endif + } + + //************************************************************************* + TEST(test_message_compare_message_id) + { + struct Message1 : public etl::message<1> {}; + struct Message2 : public etl::message<2> {}; + struct MessageNoID : public etl::imessage {}; + + CHECK_TRUE((etl::compare_message_id_less::value)); + CHECK_FALSE((etl::compare_message_id_less::value)); + + // CHECK_FALSE((etl::compare_message_id_less::value)); // Uncomment to trigger static assert. + +#if ETL_USING_CPP17 + CHECK_TRUE((etl::compare_message_id_less_v)); + CHECK_FALSE((etl::compare_message_id_less_v)); +#endif + } } diff --git a/test/test_message_router.cpp b/test/test_message_router.cpp index cfe2e9b1..2af7b989 100644 --- a/test/test_message_router.cpp +++ b/test/test_message_router.cpp @@ -138,10 +138,10 @@ namespace // Created from a type list. //*************************************************************************** #if ETL_USING_CPP17 && !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION) - using Router1Messages = etl::type_list; + using Router1Messages = etl::type_list; class Router1 : public etl::message_router_from_type_list_t #else - class Router1 : public etl::message_router + class Router1 : public etl::message_router #endif { public: @@ -397,6 +397,32 @@ namespace CHECK_EQUAL(4, r2.callback_count); } +#if !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION) + //************************************************************************* + TEST(message_router_member_types) + { + CHECK((std::is_same, Router0::message_packet>::value)); + CHECK((std::is_same, Router0::message_types>::value)); + CHECK((std::is_same, Router0::sorted_message_types>::value)); + + CHECK((std::is_same, Router1::message_packet>::value)); + CHECK((std::is_same, Router1::message_types>::value)); + CHECK((std::is_same, Router1::sorted_message_types>::value)); + + CHECK((std::is_same, Router2::message_packet>::value)); + CHECK((std::is_same, Router2::message_types>::value)); + CHECK((std::is_same, Router2::sorted_message_types>::value)); + + CHECK((std::is_same, etl::null_message_router::message_packet>::value)); + CHECK((std::is_same, etl::null_message_router::message_types>::value)); + CHECK((std::is_same, etl::null_message_router::sorted_message_types>::value)); + + CHECK((std::is_same, etl::message_producer::message_packet>::value)); + CHECK((std::is_same, etl::message_producer::message_types>::value)); + CHECK((std::is_same, etl::message_producer::sorted_message_types>::value)); + } +#endif + //************************************************************************* TEST(message_router_with_no_message_types) { @@ -411,8 +437,6 @@ namespace r0.append_successor(r1); // All messages are passed to r1. - CHECK_TRUE((etl::is_same>::value)); - CHECK(!r0.is_null_router()); CHECK(r0.is_producer()); CHECK(r0.is_consumer());