Loading...
Searching...
No Matches
message_router.h
/******************************************************************************
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 "shared_message.h"
#include "successor.h"
#include "type_list.h"
#include "type_traits.h"
#include <stddef.h>
namespace etl
{
//***************************************************************************
//***************************************************************************
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_)
{
}
};
//***************************************************************************
//***************************************************************************
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
{
#if ETL_USING_CPP11
//***************************************************************************
// Traits for a message router.
// message packet type
// message type_list
// sorted message type_list.
//***************************************************************************
template <typename... TMessageTypes>
class traits
{
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");
};
//***************************************************************************
// Specialisation of traits for no message types.
// message packet type
// message type_list
// sorted message type_list.
//***************************************************************************
template <>
class traits<>
{
public:
using message_packet = etl::message_packet<>;
using message_types = etl::type_list<>;
using sorted_message_types = etl::type_list<>;
};
#else
//***************************************************************************
// C++03 empty traits placeholder.
//***************************************************************************
template <typename T1 = void>
class traits
{
};
#endif
} // namespace private_message_router
//***************************************************************************
//***************************************************************************
class imessage_router;
etl::imessage_router& get_null_message_router();
//***************************************************************************
//***************************************************************************
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;
};
//***************************************************************************
//***************************************************************************
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;
}
};
//***********************************************
inline etl::imessage_router& get_null_message_router()
{
return etl::null_message_router::instance();
}
//***************************************************************************
//***************************************************************************
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;
}
};
//***************************************************************************
//***************************************************************************
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>
{
};
//***************************************************************************
//***************************************************************************
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);
}
//***************************************************************************
//***************************************************************************
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);
}
//***************************************************************************
//***************************************************************************
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);
}
//***************************************************************************
//***************************************************************************
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;
//**********************************************
//**********************************************
message_router()
: imessage_router(etl::imessage_router::MESSAGE_ROUTER)
{
}
//**********************************************
//**********************************************
message_router(etl::imessage_router& successor_)
: imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_)
{
}
//**********************************************
//**********************************************
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));
}
//**********************************************
//**********************************************
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));
}
//**********************************************
//**********************************************
using etl::imessage_router::receive;
//**********************************************
//***********************************************
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"
}
}
//**********************************************
//**********************************************
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"
}
//**********************************************
//**********************************************
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"
}
}
//**********************************************
//**********************************************
using imessage_router::accepts;
//**********************************************
//***********************************************
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&);
using message_dispatch_table_t = etl::array<handler_ptr,
Number_Of_Messages>;
using message_id_table_t = etl::array<etl::message_id_t,
Number_Of_Messages>;
//**********************************************
// 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:
//**********************************************
//**********************************************
message_router()
: imessage_router(etl::imessage_router::MESSAGE_ROUTER)
{
}
//**********************************************
//**********************************************
message_router(etl::imessage_router& successor_)
: imessage_router(etl::imessage_router::MESSAGE_ROUTER, successor_)
{
}
//**********************************************
//**********************************************
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));
}
//**********************************************
//**********************************************
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));
}
//**********************************************
using etl::imessage_router::receive;
//**********************************************
//***********************************************
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"
}
//**********************************************
//**********************************************
using 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 true;
}
};
//***************************************************************************
//***************************************************************************
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
alignment.h
array.h
etl::imessage_router
This is the base of all message routers.
Definition message_router.h:141
etl::imessage
Definition message.h:73
etl::message_packet
Definition message_packet.h:42
etl::message_router_exception
Base exception class for message router.
Definition message_router.h:54
etl::message_router_illegal_id
Router id is out of the legal range.
Definition message_router.h:67
etl::message_router
Definition message_router.h:46
etl::message
Definition message.h:92
etl::private_message_router::traits
Definition message_router.h:125
etl::shared_message
Definition shared_message.h:50
etl::shared_message::get_message
ETL_NODISCARD etl::imessage & get_message()
Get a reference to the contained message.
Definition shared_message.h:192
etl::successor
Adds successor traits to a class.
Definition successor.h:73
etl::successor< imessage_router >::has_successor
bool has_successor() const
Definition successor.h:184
etl::successor< imessage_router >::get_successor
successor_type & get_successor() const
Definition successor.h:174
message_packet
Definition message_packet_cpp03.h:41
diagnostic_array_bounds_push.h
diagnostic_pop.h
error_handler.h
exception.h
ETL_ASSERT
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
etl::exception
Definition exception.h:59
type
largest.h
etl
Definition absolute.h:40
etl::get_null_message_router
etl::imessage_router & get_null_message_router()
null message router functionality.
Definition message_router.h:295
etl::message_id_t
uint_least8_t message_id_t
Allow alternative type for message id.
Definition message_types.h:40
platform.h
shared_message.h
etl::bool_constant
Definition type_traits.h:97
etl::is_message_router
Is T ultimately derived from etl::imessage_router?
Definition message_router.h:387
etl::type
successor.h
type_traits.h