897 lines
32 KiB
C++
897 lines
32 KiB
C++
/******************************************************************************
|
|
The MIT License(MIT)
|
|
|
|
Embedded Template Library.
|
|
https://github.com/ETLCPP/etl
|
|
https://www.etlcpp.com
|
|
|
|
Copyright(c) 2017 John Wellbelove
|
|
|
|
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.
|
|
******************************************************************************/
|
|
|
|
#ifndef ETL_MESSAGE_ROUTER_INCLUDED
|
|
#define ETL_MESSAGE_ROUTER_INCLUDED
|
|
|
|
#include "platform.h"
|
|
#include "alignment.h"
|
|
#include "array.h"
|
|
#include "error_handler.h"
|
|
#include "exception.h"
|
|
#include "largest.h"
|
|
#include "message.h"
|
|
#include "message_packet.h"
|
|
#include "message_types.h"
|
|
#include "nullptr.h"
|
|
#include "placement_new.h"
|
|
#include "shared_message.h"
|
|
#include "successor.h"
|
|
#include "type_list.h"
|
|
#include "type_traits.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
namespace etl
|
|
{
|
|
//***************************************************************************
|
|
/// Base exception class for message router
|
|
//***************************************************************************
|
|
class message_router_exception : public etl::exception
|
|
{
|
|
public:
|
|
|
|
message_router_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
|
|
: etl::exception(reason_, file_name_, line_number_)
|
|
{
|
|
}
|
|
};
|
|
|
|
//***************************************************************************
|
|
/// Router id is out of the legal range.
|
|
//***************************************************************************
|
|
class message_router_illegal_id : public etl::message_router_exception
|
|
{
|
|
public:
|
|
|
|
message_router_illegal_id(string_type file_name_, numeric_type line_number_)
|
|
: message_router_exception(ETL_ERROR_TEXT("message router:illegal id", ETL_MESSAGE_ROUTER_FILE_ID"A"), file_name_, line_number_)
|
|
{
|
|
}
|
|
};
|
|
|
|
namespace private_message_router
|
|
{
|
|
//***************************************************************************
|
|
// Traits for a message router.
|
|
// message packet type
|
|
// message type_list
|
|
// sorted message type_list.
|
|
//***************************************************************************
|
|
template <typename... TMessageTypes>
|
|
class traits
|
|
{
|
|
#if ETL_USING_CPP11
|
|
|
|
private:
|
|
|
|
using message_id_sequence = etl::index_sequence<TMessageTypes::ID...>;
|
|
|
|
public:
|
|
|
|
using message_packet = etl::message_packet<TMessageTypes...>;
|
|
using message_types = etl::type_list<TMessageTypes...>;
|
|
using sorted_message_types = etl::type_list_sort_t<message_types, etl::compare_message_id_less>;
|
|
|
|
static_assert(etl::type_list_is_unique<message_types>::value, "All TMessageTypes must be unique");
|
|
static_assert(etl::type_list_all_of<message_types, etl::is_message_type>::value,
|
|
"All TMessageTypes must satisfy the condition etl::is_message_type");
|
|
static_assert(etl::index_sequence_is_unique<message_id_sequence>::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
|
|
};
|
|
} // namespace private_message_router
|
|
|
|
//***************************************************************************
|
|
/// Forward declare null message router functionality.
|
|
//***************************************************************************
|
|
class imessage_router;
|
|
|
|
etl::imessage_router& get_null_message_router();
|
|
|
|
//***************************************************************************
|
|
/// This is the base of all message routers.
|
|
//***************************************************************************
|
|
class imessage_router : public etl::successor<imessage_router>
|
|
{
|
|
public:
|
|
|
|
virtual ~imessage_router() {}
|
|
virtual void receive(const etl::imessage&) = 0;
|
|
virtual bool accepts(etl::message_id_t) const = 0;
|
|
virtual bool is_null_router() const = 0;
|
|
virtual bool is_producer() const = 0;
|
|
virtual bool is_consumer() const = 0;
|
|
|
|
//********************************************
|
|
virtual void receive(etl::message_router_id_t destination_router_id, const etl::imessage& message)
|
|
{
|
|
if ((destination_router_id == get_message_router_id()) || (destination_router_id == imessage_router::ALL_MESSAGE_ROUTERS))
|
|
{
|
|
receive(message);
|
|
}
|
|
}
|
|
|
|
//********************************************
|
|
virtual void receive(etl::shared_message shared_msg)
|
|
{
|
|
receive(shared_msg.get_message());
|
|
}
|
|
|
|
//********************************************
|
|
virtual void receive(etl::message_router_id_t destination_router_id, etl::shared_message shared_msg)
|
|
{
|
|
if ((destination_router_id == get_message_router_id()) || (destination_router_id == imessage_router::ALL_MESSAGE_ROUTERS))
|
|
{
|
|
receive(shared_msg);
|
|
}
|
|
}
|
|
|
|
//********************************************
|
|
bool accepts(const etl::imessage& msg) const
|
|
{
|
|
return accepts(msg.get_message_id());
|
|
}
|
|
|
|
//********************************************
|
|
etl::message_router_id_t get_message_router_id() const
|
|
{
|
|
return message_router_id;
|
|
}
|
|
|
|
enum
|
|
{
|
|
NULL_MESSAGE_ROUTER = 255,
|
|
MESSAGE_BUS = 254,
|
|
ALL_MESSAGE_ROUTERS = 253,
|
|
MESSAGE_BROKER = 252,
|
|
MESSAGE_ROUTER = 251,
|
|
MAX_MESSAGE_ROUTER = 249
|
|
};
|
|
|
|
protected:
|
|
|
|
imessage_router(etl::message_router_id_t id_)
|
|
: message_router_id(id_)
|
|
{
|
|
}
|
|
|
|
imessage_router(etl::message_router_id_t id_, imessage_router& successor_)
|
|
: successor(successor_)
|
|
, message_router_id(id_)
|
|
{
|
|
}
|
|
|
|
private:
|
|
|
|
// Disabled.
|
|
imessage_router(const imessage_router&);
|
|
imessage_router& operator=(const imessage_router&);
|
|
|
|
etl::message_router_id_t message_router_id;
|
|
};
|
|
|
|
//***************************************************************************
|
|
/// 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:
|
|
|
|
//********************************************
|
|
null_message_router()
|
|
: imessage_router(imessage_router::NULL_MESSAGE_ROUTER)
|
|
{
|
|
}
|
|
|
|
//********************************************
|
|
null_message_router(etl::imessage_router& successor_)
|
|
: imessage_router(imessage_router::NULL_MESSAGE_ROUTER, successor_)
|
|
{
|
|
}
|
|
|
|
//********************************************
|
|
using etl::imessage_router::receive;
|
|
|
|
void receive(const etl::imessage& msg) ETL_OVERRIDE
|
|
{
|
|
if (has_successor())
|
|
{
|
|
get_successor().receive(msg);
|
|
}
|
|
}
|
|
|
|
//********************************************
|
|
using etl::imessage_router::accepts;
|
|
|
|
bool accepts(etl::message_id_t id) const ETL_OVERRIDE
|
|
{
|
|
if (has_successor())
|
|
{
|
|
return get_successor().accepts(id);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//********************************************
|
|
ETL_DEPRECATED
|
|
bool is_null_router() const ETL_OVERRIDE
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_producer() const ETL_OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_consumer() const ETL_OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//********************************************
|
|
static null_message_router& instance()
|
|
{
|
|
static null_message_router nmr;
|
|
return nmr;
|
|
}
|
|
};
|
|
|
|
//***********************************************
|
|
/// null message router functionality.
|
|
inline etl::imessage_router& get_null_message_router()
|
|
{
|
|
return etl::null_message_router::instance();
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// 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:
|
|
|
|
//********************************************
|
|
message_producer()
|
|
: imessage_router(etl::imessage_router::MESSAGE_ROUTER)
|
|
{
|
|
}
|
|
|
|
//********************************************
|
|
message_producer(etl::imessage_router& successor_)
|
|
: imessage_router(imessage_router::NULL_MESSAGE_ROUTER, successor_)
|
|
{
|
|
}
|
|
|
|
//********************************************
|
|
message_producer(etl::message_router_id_t id_)
|
|
: imessage_router(id_)
|
|
{
|
|
ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id));
|
|
}
|
|
|
|
//********************************************
|
|
message_producer(etl::message_router_id_t id_, etl::imessage_router& successor_)
|
|
: imessage_router(id_, successor_)
|
|
{
|
|
ETL_ASSERT(id_ <= etl::imessage_router::MAX_MESSAGE_ROUTER, ETL_ERROR(etl::message_router_illegal_id));
|
|
}
|
|
|
|
//********************************************
|
|
using etl::imessage_router::receive;
|
|
|
|
void receive(const etl::imessage& msg) ETL_OVERRIDE
|
|
{
|
|
if (has_successor())
|
|
{
|
|
get_successor().receive(msg);
|
|
}
|
|
}
|
|
|
|
//********************************************
|
|
using etl::imessage_router::accepts;
|
|
|
|
bool accepts(etl::message_id_t id) const ETL_OVERRIDE
|
|
{
|
|
if (has_successor())
|
|
{
|
|
return get_successor().accepts(id);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//********************************************
|
|
ETL_DEPRECATED
|
|
bool is_null_router() const ETL_OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_producer() const ETL_OVERRIDE
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_consumer() const ETL_OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
};
|
|
|
|
//***************************************************************************
|
|
/// Is T ultimately derived from etl::imessage_router?
|
|
//***************************************************************************
|
|
template <typename T>
|
|
struct is_message_router : public etl::bool_constant< etl::is_base_of< etl::imessage_router, typename etl::remove_cvref<T>::type>::value>
|
|
{
|
|
};
|
|
|
|
//***************************************************************************
|
|
/// Send a message to a router.
|
|
//***************************************************************************
|
|
template <typename TRouter, typename TMessage>
|
|
static typename etl::enable_if< etl::is_message_router<TRouter>::value && etl::is_message<TMessage>::value, void>::type
|
|
send_message(TRouter& destination, const TMessage& message)
|
|
{
|
|
destination.receive(message);
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Send a shared message to a router.
|
|
//***************************************************************************
|
|
template <typename TRouter>
|
|
static typename etl::enable_if<etl::is_message_router<TRouter>::value, void>::type send_message(TRouter& destination, etl::shared_message message)
|
|
{
|
|
destination.receive(message);
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Send a message to a router with a particular id.
|
|
//***************************************************************************
|
|
template <typename TRouter, typename TMessage>
|
|
static typename etl::enable_if< etl::is_message_router<TRouter>::value && etl::is_message<TMessage>::value, void>::type
|
|
send_message(TRouter& destination, etl::message_router_id_t id, const TMessage& message)
|
|
{
|
|
destination.receive(id, message);
|
|
}
|
|
|
|
//***************************************************************************
|
|
/// Send a shared message to a router with a particular id.
|
|
//***************************************************************************
|
|
template <typename TRouter>
|
|
static typename etl::enable_if<etl::is_message_router<TRouter>::value, void>::type send_message(TRouter& destination, etl::message_router_id_t id,
|
|
etl::shared_message message)
|
|
{
|
|
destination.receive(id, message);
|
|
}
|
|
|
|
//*************************************************************************************************
|
|
// For C++11 and above.
|
|
//*************************************************************************************************
|
|
#if ETL_USING_CPP11 && !defined(ETL_MESSAGE_ROUTER_FORCE_CPP03_IMPLEMENTATION)
|
|
//***************************************************************************
|
|
// The definition for all message types.
|
|
//***************************************************************************
|
|
template <typename TDerived, typename... TMessageTypes>
|
|
class message_router
|
|
: public imessage_router
|
|
, public private_message_router::traits<TMessageTypes...>
|
|
{
|
|
public:
|
|
|
|
using typename private_message_router::traits<TMessageTypes...>::message_packet;
|
|
using typename private_message_router::traits<TMessageTypes...>::message_types;
|
|
using typename private_message_router::traits< TMessageTypes...>::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_)
|
|
{
|
|
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_)
|
|
{
|
|
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 etl::message_id_t id = msg.get_message_id();
|
|
|
|
// The IDs are sorted, so an ID less than the first is not handled by this
|
|
// router.
|
|
if (id >= Message_Id_Start)
|
|
{
|
|
const size_t 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);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
else
|
|
{
|
|
#include "etl/private/diagnostic_array_bounds_push.h"
|
|
static_cast<TDerived*>(this)->on_receive_unknown(msg);
|
|
#include "etl/private/diagnostic_pop.h"
|
|
}
|
|
}
|
|
|
|
//**********************************************
|
|
/// 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 < typename TMessage, typename etl::enable_if<etl::is_one_of<TMessage, TMessageTypes...>::value, int>::type = 0>
|
|
void receive(const TMessage& msg)
|
|
{
|
|
#include "etl/private/diagnostic_array_bounds_push.h"
|
|
static_cast<TDerived*>(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 <typename TMessage,
|
|
typename etl::enable_if< etl::is_message<TMessage>::value && !etl::is_one_of<TMessage, TMessageTypes...>::value, int>::type = 0>
|
|
void receive(const TMessage& msg)
|
|
{
|
|
if (has_successor())
|
|
{
|
|
get_successor().receive(msg);
|
|
}
|
|
else
|
|
{
|
|
#include "etl/private/diagnostic_array_bounds_push.h"
|
|
static_cast<TDerived*>(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
|
|
{
|
|
if (id >= Message_Id_Start)
|
|
{
|
|
const size_t index = get_dispatch_index_from_message_id(id);
|
|
|
|
if (index < Number_Of_Messages)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return has_successor() ? get_successor().accepts(id) : false;
|
|
}
|
|
|
|
//********************************************
|
|
ETL_DEPRECATED
|
|
bool is_null_router() const ETL_OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_producer() const ETL_OVERRIDE
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_consumer() const ETL_OVERRIDE
|
|
{
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
|
|
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<sorted_message_types, 0>::ID;
|
|
|
|
//**********************************************
|
|
// Checks that the message ids are contiguous.
|
|
//**********************************************
|
|
template <size_t Index, bool Last = (Index + 1U >= Number_Of_Messages)>
|
|
struct contiguous_impl;
|
|
|
|
template <size_t Index>
|
|
struct contiguous_impl<Index, true> : etl::true_type
|
|
{
|
|
};
|
|
|
|
template <size_t Index>
|
|
struct contiguous_impl<Index, false>
|
|
: etl::bool_constant< (etl::type_list_type_at_index_t<sorted_message_types, Index>::ID + 1U
|
|
== etl::type_list_type_at_index_t<sorted_message_types, Index + 1U>::ID)
|
|
&& contiguous_impl<Index + 1U>::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<handler_ptr,
|
|
Number_Of_Messages>; ///< The dispatch table type. An array of
|
|
///< handler pointers, one for each
|
|
///< message type.
|
|
using message_id_table_t = etl::array<etl::message_id_t,
|
|
Number_Of_Messages>; ///< The message id table type. An
|
|
///< array of message ids, one for
|
|
///< each message type.
|
|
|
|
//**********************************************
|
|
// Call for a single message type
|
|
//**********************************************
|
|
template <typename TMessage>
|
|
static void call_on_receive(TDerived& derived, const imessage& msg)
|
|
{
|
|
derived.on_receive(static_cast<const TMessage&>(msg));
|
|
}
|
|
|
|
//**********************************************
|
|
// 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 <size_t Index>
|
|
static constexpr handler_ptr get_message_handler()
|
|
{
|
|
return &call_on_receive< etl::type_list_type_at_index_t<sorted_message_types, Index>>;
|
|
}
|
|
|
|
//**********************************************
|
|
// Generate the dispatch table at compile time.
|
|
// This will create an array of handler pointers, one for each message type.
|
|
//**********************************************
|
|
template <size_t... Indices>
|
|
static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence<Indices...>)
|
|
{
|
|
return message_dispatch_table_t{{get_message_handler<Indices>()...}};
|
|
}
|
|
|
|
//**********************************************
|
|
// 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 <size_t Index>
|
|
static constexpr etl::message_id_t get_message_id_from_index()
|
|
{
|
|
return etl::type_list_type_at_index_t<sorted_message_types, Index>::ID;
|
|
}
|
|
|
|
//**********************************************
|
|
// Generate the message id table at compile time.
|
|
// This will create an array of message ids, one for each message type.
|
|
//**********************************************
|
|
template <size_t... Indices>
|
|
static constexpr message_id_table_t make_message_id_table(etl::index_sequence<Indices...>)
|
|
{
|
|
return message_id_table_t{{get_message_id_from_index<Indices>()...}};
|
|
}
|
|
|
|
//**********************************************
|
|
// 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)
|
|
{
|
|
// The IDs are contiguous, so we can calculate the index directly.
|
|
return static_cast<size_t>(id - Message_Id_Start);
|
|
}
|
|
else
|
|
{
|
|
// 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)
|
|
{
|
|
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<TDerived&>(*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<TDerived, TMessageTypes...>::make_message_dispatch_table(
|
|
etl::make_index_sequence< etl::message_router< TDerived, TMessageTypes...>::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<TDerived, TMessageTypes...>::make_message_id_table(
|
|
etl::make_index_sequence< etl::message_router< TDerived, TMessageTypes...>::Number_Of_Messages>{});
|
|
};
|
|
|
|
#if ETL_USING_CPP11 && !ETL_USING_CPP17
|
|
template <typename TDerived, typename... TMessageTypes>
|
|
constexpr const typename etl::message_router< TDerived, TMessageTypes...>::message_dispatch_table_t
|
|
etl::message_router<TDerived, TMessageTypes...>::message_dispatch_table;
|
|
|
|
template <typename TDerived, typename... TMessageTypes>
|
|
constexpr const typename etl::message_router< TDerived, TMessageTypes...>::message_id_table_t
|
|
etl::message_router<TDerived, TMessageTypes...>::message_id_table;
|
|
#endif
|
|
|
|
//***************************************************************************
|
|
// The definition of a message_router for zero message types.
|
|
//***************************************************************************
|
|
template <typename TDerived>
|
|
class message_router<TDerived>
|
|
: public imessage_router
|
|
, public private_message_router::traits<>
|
|
{
|
|
public:
|
|
|
|
//**********************************************
|
|
/// 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_)
|
|
{
|
|
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_)
|
|
{
|
|
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.
|
|
/// 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
|
|
{
|
|
#include "etl/private/diagnostic_array_bounds_push.h"
|
|
if (has_successor())
|
|
{
|
|
get_successor().receive(msg);
|
|
}
|
|
#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())
|
|
{
|
|
return get_successor().accepts(id);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//********************************************
|
|
ETL_DEPRECATED
|
|
bool is_null_router() const ETL_OVERRIDE
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_producer() const ETL_OVERRIDE
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//********************************************
|
|
bool is_consumer() const ETL_OVERRIDE
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//***************************************************************************
|
|
/// Helper to turn etl::type_list<TTypes...> into
|
|
/// etl::message_router<TTypes...>
|
|
//***************************************************************************
|
|
template <typename TDerived, typename TList>
|
|
struct message_router_from_type_list;
|
|
|
|
template <typename TDerived, typename... TMessageTypes>
|
|
struct message_router_from_type_list<TDerived, etl::type_list<TMessageTypes...>>
|
|
{
|
|
using type = etl::message_router<TDerived, TMessageTypes...>;
|
|
};
|
|
|
|
template <typename TDerived, typename TTypeList>
|
|
using message_router_from_type_list_t = typename message_router_from_type_list<TDerived, TTypeList>::type;
|
|
|
|
#else
|
|
#include "private/message_router_cpp03.h"
|
|
#endif
|
|
} // namespace etl
|
|
|
|
#endif
|