Enforce o(log n) dispatch for messages when using fsm for c++11 and up (#1337)

* Updated message handling to be worst case O(logN)

* Copied optimised message handling from etl::fsm

* Updated fsm generator

* Updated message_router generator

* Added optimised accepts() member function

* Modified comment, as the FSM doesn't support a successor

* Updated version and release notes

* Hotfix/etl  multiset iterator invalidation during erase leads to incorrect sorted order in depth first traversal (#1317)

* Fixed issue for both multiset and multimap

* Added std::is_sorted checks to all map/set tests

* Updated with coderabbit suggestions

---------

Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.com>

* Updated release notes and version

* Changed std::is_same to etl::is_same in struct type_list_is_unique (#1320)

Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.com>

* Updated release notes and version

* Fix etl::rotate (#1327)

Per the C++ standard, std::rotate returns first + (last - middle):

* When first == middle, return last
* When middle == last, return first

* Added missing files from VS2022 project

* Fix greater_equal and less_equal (#1331)

* Align comparison operators (#1330)

In functional.h, the comparison operators for equal_to and not_equal_to
mismatch between the actual comparison execution and the type inference
for the return type. This change adjusts it by using the same operator==()
in the return type inference as used in the comparison execution.

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>

* Add missing tests (#1321)

* Add missing tests

* Typo fixes

---------

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>

* Add ETL_FORMAT_NO_FLOATING_POINT control macro for etl::format (#1329)

When ETL_FORMAT_NO_FLOATING_POINT is defined, all floating-point formatting support (float, double, long double) is excluded from etl::format. This reduces code size on targets that do not require floating-point formatting.

Guarded sections:

- #include <cmath>

- float/double/long double in supported_format_types variant

- float/double/long double constructors in basic_format_arg

- format_floating_* functions and format_aligned_floating

- formatter<float>, formatter<double>, formatter<long double>

- Floating-point test cases in test_format.cpp

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>

* Manchester documentation (#1325)

* manchester
* Added manchester code and test

* manchester
* Formatting and added missing file

* manchester
* Some functions can only be constexpr since C++14

* manchester
* Manchester decode and some refactoring

* manchester
* Added some missing typenames

* manchester
* constexpr void function not allowed in C++11

* manchester
* condition on static_assert tests

* manchester
* revert CMakeLists.txt
* Using ETL_STATIC_ASSERT
* Some cleanup

* manchester
* Added static_assert message

* manchester
* Added compile time tests

* manchester
* Added invert manchester
* Some refactoring

* manchester
* Disable test for now
* Move ETL_NODISCARD before static

* manchester
* Test for valid_span

* manchester
* Remove redundant (?) storage specifiers for template specializations. Storage specifier already given in base template

* manchester
* refactoring to get rid of specialized template functions in template class

* manchester
* cleanup

* manchester
* Added documentation comments
* Some refactoring

* manchester
* introducing namespace detail_manchester

* manchester
* Some refactoring
* Update tests

* manchester
* Some refactoring
* Removed possible undefined behavior by refactoring encode_span
* constexpr version of encode_span
* Static assertion for rare case where code doesn't work because CHAR_BIT is not the same as the number of bits in uint_least8_t

* manchester
* renamed valid to is_valid

* manchester
* renamed is_valid_span to is_valid
* Using etl exceptions in ETL_ASSERT

* manchester
* Removed _fast functions
* merged encode_in_place with encode and decode_in_place with decode
* removed _span to create normal overloads of encode and decode for span
* Some renaming and minor refactoring

* manchester
* Fix build issues

* manchester
* Conditionally compile manchester_decoded

* Update test_manchester.cpp

Removed redundant semicolon

* #1258 Manchester coding
* Formatting
* consistency: hex literals with lower case 0x

* #1258 Manchester coding
* Moved copyright to top of file
* Make constexpr encode/decode span functions equal for little and big endian platforms

* #1258 Manchester coding
* Added missing include
* Added missing 8bit/64bit guards
* Fixed is_valid for big endian platforms

* #1258 Manchester coding
* private memcpy alias

* #1258 Manchester coding
* Review comments

* #1258 Manchester coding
* Cleanup
* Fix build error

* #1258 Manchester coding
* Add manchester documentation

* #1258 Manchester coding
* Preparation for GitHub pages

* #1324 Manchester documentation
* Some small fixes

---------

Co-authored-by: Timon Zijnge <timon.zijnge@imec.nl>

* Changes from review of algorithm.h on development branch (#1340)

* Add missing constexpr in algorithm.h

* Fix call of nth_element

2nd argument (nth) was missing

* Replace partition point with O(log(N)) algorithm

The C++ standard defines O(log(N)) calls of predicate as the
complexity of partition_point(). The old algorithm was linear.

* Use predicate in calculation of is_permutation consistently

In case of predicate not equal_to, the calculation previously
returned wron results

* Omit swap in selection_sort if iterators are equal

* Use difference_type in rotate_general() instead of int

* Typo fix in algorithm.h

* Simplifications in algorithm.h

Application of plain refactoring by keeping semantics

* Guard against past-end iterator in etl::rotate()

And fix scope of rotate_right_by_one for etl::rotate()

* Support empty ranges in selection_sort

* Add tests for swap_ranges

* Add tests for binary_search

* Add tests for find_end

* Add tests for accumulate

* Add tests for move_s

* Added tests for is_heap and sort_heap

* Remove early exit for empty input

* Add adjacent_find

* Add unique

* Add unique_copy

* Add merge

* Add inplace_merge

* Add partial_sort

* Add partial_sort_copy

* copilot review change

---------

Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.com>
Co-authored-by: Roland Reichwein <Roland.Reichwein@bmw.de>
Co-authored-by: Niu Zhihong <zhihong@nzhnb.com>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: Timon Zijnge <47081647+tzijnge@users.noreply.github.com>
Co-authored-by: Timon Zijnge <timon.zijnge@imec.nl>
This commit is contained in:
John Wellbelove 2026-03-12 17:06:26 +00:00 committed by GitHub
parent 0a56d40bdd
commit d3affac417
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 633 additions and 265 deletions

View File

@ -62,6 +62,7 @@ SOFTWARE.
#include "largest.h"
#if ETL_USING_CPP11
#include "tuple.h"
#include "type_list.h"
#endif
#include <stdint.h>
@ -83,14 +84,14 @@ namespace etl
// For internal FSM use.
typedef typename etl::larger_type<etl::message_id_t>::type fsm_internal_id_t;
#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above
#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
template <typename, typename, etl::fsm_state_id_t, typename...>
class fsm_state;
#else
template <typename, typename, etl::fsm_state_id_t,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename>
class fsm_state;
#endif
@ -195,7 +196,7 @@ namespace etl
// Pass this whenever no state change is desired.
// The highest unsigned value of fsm_state_id_t.
static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits<fsm_state_id_t>::max;
// Pass this when this event also needs to be passed to the parent.
static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U;
@ -213,15 +214,15 @@ namespace etl
ETL_CONSTANT fsm_state_id_t ifsm_state_helper<T>::Self_Transition;
// Compile-time: TState::ID must equal its index in the type list (0..N-1)
template <size_t Id, typename...> struct check_ids : etl::true_type
template <size_t Id, typename...> struct check_ids : etl::true_type
{
};
template <size_t Id, typename TState0, typename... TRest>
struct check_ids<Id, TState0, TRest...>
: etl::integral_constant<bool, (TState0::STATE_ID == Id) && private_fsm::check_ids<Id + 1, TRest...>::value>
: etl::integral_constant<bool, (TState0::STATE_ID == Id) && private_fsm::check_ids<Id + 1, TRest...>::value>
{
};
};
//***************************************************************************
/// RAII detection mechanism to catch reentrant calls to methods that might
@ -250,11 +251,11 @@ namespace etl
{
is_locked = false;
}
private:
// Reference to the flag signifying a lock on the state machine.
bool& is_locked;
// Copy & move semantics disabled since this is a guard.
fsm_reentrancy_guard(fsm_reentrancy_guard const&) ETL_DELETE;
fsm_reentrancy_guard& operator= (fsm_reentrancy_guard const&) ETL_DELETE;
@ -272,7 +273,7 @@ namespace etl
/// A class to store FSM states.
//***************************************************************************
template <typename... TStates>
class fsm_state_pack
class fsm_state_pack
{
public:
@ -294,18 +295,18 @@ namespace etl
/// Gets a reference to the state.
//*********************************
template <typename TState>
TState& get()
{
return etl::get<TState>(storage);
TState& get()
{
return etl::get<TState>(storage);
}
//*********************************
/// Gets a const reference to the state.
//*********************************
template <typename TState>
const TState& get() const
{
return etl::get<TState>(storage);
const TState& get() const
{
return etl::get<TState>(storage);
}
private:
@ -341,14 +342,14 @@ namespace etl
using private_fsm::ifsm_state_helper<>::Pass_To_Parent;
using private_fsm::ifsm_state_helper<>::Self_Transition;
#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above
#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
template <typename, typename, etl::fsm_state_id_t, typename...>
friend class fsm_state;
#else
template <typename, typename, etl::fsm_state_id_t,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename,
typename, typename, typename, typename>
friend class etl::fsm_state;
#endif
@ -560,7 +561,7 @@ namespace etl
{
etl::fsm_state_id_t next_state_id = p_state->process_event(message);
process_state_change(next_state_id);
process_state_change(next_state_id);
}
else
{
@ -691,7 +692,7 @@ namespace etl
p_state->on_exit_state();
next_state_id = p_state->on_enter_state();
}
if (have_changed_state(next_state_id))
{
ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id());
@ -722,17 +723,249 @@ namespace etl
};
//*************************************************************************************************
// For C++17 and above.
// For C++11 and above.
//*************************************************************************************************
#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above
#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
//***************************************************************************
// The definition for all types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
class fsm_state : public ifsm_state
{
private:
using message_id_sequence = etl::index_sequence<TMessageTypes::ID...>;
public:
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");
static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
fsm_state()
: ifsm_state(STATE_ID)
{
}
protected:
~fsm_state()
{
}
TContext& get_fsm_context() const
{
return static_cast<TContext&>(ifsm_state::get_fsm_context());
}
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;
static_assert(Number_Of_Messages > 0, "Zero messages");
//**********************************************
// 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 = etl::fsm_state_id_t (*)(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.
//********************************************
etl::fsm_state_id_t process_event(const etl::imessage& message)
{
const etl::message_id_t id = message.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)
{
const etl::fsm_state_id_t new_state_id = dispatch(message, index);
if (new_state_id != Pass_To_Parent)
{
return new_state_id;
}
}
}
#include "etl/private/diagnostic_array_bounds_push.h"
// If we get here, then we don't have a handler for this message type, so pass it to the parent if we have one, otherwise call on_event_unknown.
return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
#include "etl/private/diagnostic_pop.h"
}
//**********************************************
// Call for a single message type
//**********************************************
template <typename TMessage>
static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg)
{
return derived.on_event(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_event<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.
//**********************************************
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.
//**********************************************
etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index)
{
return 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::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_dispatch_table(etl::make_index_sequence<etl::fsm_state<TContext, TDerived, STATE_ID_, 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::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_id_table(etl::make_index_sequence<etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::Number_Of_Messages>{});
};
#if ETL_USING_CPP11 && !ETL_USING_CPP17
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
constexpr const typename etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table_t
etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table;
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
constexpr const typename etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table_t
etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table;
#endif
/// Definition of STATE_ID
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::STATE_ID;
//***************************************************************************
// The definition for no messages.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_>
class fsm_state<TContext, TDerived, STATE_ID_> : public ifsm_state
{
private:
using message_id_sequence = etl::index_sequence<>;
public:
using message_types = etl::type_list<>;
using sorted_message_types = etl::type_list<>;
static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
fsm_state()
@ -753,59 +986,24 @@ namespace etl
private:
//********************************************
struct result_t
{
bool was_handled;
etl::fsm_state_id_t state_id;
};
//********************************************
etl::fsm_state_id_t process_event(const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
const bool was_handled = (process_event_type<TMessageTypes>(message, new_state_id) || ...);
if (!was_handled || (new_state_id == Pass_To_Parent))
{
new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
}
return new_state_id;
}
//********************************************
template <typename TMessage>
bool process_event_type(const etl::imessage& msg, etl::fsm_state_id_t& new_state_id)
{
if (TMessage::ID == msg.get_message_id())
{
new_state_id = static_cast<TDerived*>(this)->on_event(static_cast<const TMessage&>(msg));
return true;
}
else
{
return false;
}
return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
}
};
/// Definition of STATE_ID
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::STATE_ID;
#else
//*************************************************************************************************
// For C++14 and below.
// For C++03 and below.
//*************************************************************************************************
//***************************************************************************
// The definition for all 16 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1 = void, typename T2 = void, typename T3 = void, typename T4 = void,
typename T5 = void, typename T6 = void, typename T7 = void, typename T8 = void,
typename T9 = void, typename T10 = void, typename T11 = void, typename T12 = void,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1 = void, typename T2 = void, typename T3 = void, typename T4 = void,
typename T5 = void, typename T6 = void, typename T7 = void, typename T8 = void,
typename T9 = void, typename T10 = void, typename T11 = void, typename T12 = void,
typename T13 = void, typename T14 = void, typename T15 = void, typename T16 = void>
class fsm_state : public ifsm_state
{
@ -864,10 +1062,10 @@ namespace etl
//***************************************************************************
// Specialisation for 15 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
typename T13, typename T14, typename T15>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, void> : public ifsm_state
{
@ -925,10 +1123,10 @@ namespace etl
//***************************************************************************
// Specialisation for 14 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
typename T13, typename T14>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, void, void> : public ifsm_state
{
@ -985,10 +1183,10 @@ namespace etl
//***************************************************************************
// Specialisation for 13 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
typename T13>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, void, void, void> : public ifsm_state
{
@ -1044,9 +1242,9 @@ namespace etl
//***************************************************************************
// Specialisation for 12 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, void, void, void, void> : public ifsm_state
{
@ -1101,9 +1299,9 @@ namespace etl
//***************************************************************************
// Specialisation for 11 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, void, void, void, void, void> : public ifsm_state
{
@ -1157,9 +1355,9 @@ namespace etl
//***************************************************************************
// Specialisation for 10 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, void, void, void, void, void, void> : public ifsm_state
{
@ -1212,9 +1410,9 @@ namespace etl
//***************************************************************************
// Specialisation for 9 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1266,8 +1464,8 @@ namespace etl
//***************************************************************************
// Specialisation for 8 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1318,8 +1516,8 @@ namespace etl
//***************************************************************************
// Specialisation for 7 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1369,8 +1567,8 @@ namespace etl
//***************************************************************************
// Specialisation for 6 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, void, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1419,8 +1617,8 @@ namespace etl
//***************************************************************************
// Specialisation for 5 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, void, void, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1468,7 +1666,7 @@ namespace etl
//***************************************************************************
// Specialisation for 4 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, void, void, void, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1515,7 +1713,7 @@ namespace etl
//***************************************************************************
// Specialisation for 3 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, void, void, void, void, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1561,7 +1759,7 @@ namespace etl
//***************************************************************************
// Specialisation for 2 message types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2>
class fsm_state<TContext, TDerived, STATE_ID_, T1, T2, void, void, void, void, void, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1606,7 +1804,7 @@ namespace etl
//***************************************************************************
// Specialisation for 1 message type.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1>
class fsm_state<TContext, TDerived, STATE_ID_, T1, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
@ -1680,10 +1878,10 @@ namespace etl
}
};
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,
typename T1, typename T2, typename T3, typename T4,
typename T5, typename T6, typename T7, typename T8,
typename T9, typename T10, typename T11, typename T12,
typename T13, typename T14, typename T15, typename T16>
ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>::STATE_ID;
#endif

View File

@ -74,6 +74,7 @@ cog.outl("//********************************************************************
#include "largest.h"
#if ETL_USING_CPP11
#include "tuple.h"
#include "type_list.h"
#endif
#include <stdint.h>
@ -95,20 +96,20 @@ namespace etl
// For internal FSM use.
typedef typename etl::larger_type<etl::message_id_t>::type fsm_internal_id_t;
#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above
#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
template <typename, typename, etl::fsm_state_id_t, typename...>
class fsm_state;
#else
/*[[[cog
import cog
cog.outl("template <typename, typename, etl::fsm_state_id_t,")
cog.out(" ")
cog.out(" ")
for n in range(1, int(Handlers)):
cog.out("typename, ")
cog.out(" typename,")
if n % 4 == 0:
cog.outl("")
cog.out(" ")
cog.outl("typename>")
cog.out(" ")
cog.outl(" typename>")
cog.outl("class fsm_state;")
]]]*/
/*[[[end]]]*/
@ -214,7 +215,7 @@ namespace etl
// Pass this whenever no state change is desired.
// The highest unsigned value of fsm_state_id_t.
static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits<fsm_state_id_t>::max;
// Pass this when this event also needs to be passed to the parent.
static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U;
@ -232,15 +233,15 @@ namespace etl
ETL_CONSTANT fsm_state_id_t ifsm_state_helper<T>::Self_Transition;
// Compile-time: TState::ID must equal its index in the type list (0..N-1)
template <size_t Id, typename...> struct check_ids : etl::true_type
template <size_t Id, typename...> struct check_ids : etl::true_type
{
};
template <size_t Id, typename TState0, typename... TRest>
struct check_ids<Id, TState0, TRest...>
: etl::integral_constant<bool, (TState0::STATE_ID == Id) && private_fsm::check_ids<Id + 1, TRest...>::value>
: etl::integral_constant<bool, (TState0::STATE_ID == Id) && private_fsm::check_ids<Id + 1, TRest...>::value>
{
};
};
//***************************************************************************
/// RAII detection mechanism to catch reentrant calls to methods that might
@ -269,11 +270,11 @@ namespace etl
{
is_locked = false;
}
private:
// Reference to the flag signifying a lock on the state machine.
bool& is_locked;
// Copy & move semantics disabled since this is a guard.
fsm_reentrancy_guard(fsm_reentrancy_guard const&) ETL_DELETE;
fsm_reentrancy_guard& operator= (fsm_reentrancy_guard const&) ETL_DELETE;
@ -291,7 +292,7 @@ namespace etl
/// A class to store FSM states.
//***************************************************************************
template <typename... TStates>
class fsm_state_pack
class fsm_state_pack
{
public:
@ -313,18 +314,18 @@ namespace etl
/// Gets a reference to the state.
//*********************************
template <typename TState>
TState& get()
{
return etl::get<TState>(storage);
TState& get()
{
return etl::get<TState>(storage);
}
//*********************************
/// Gets a const reference to the state.
//*********************************
template <typename TState>
const TState& get() const
{
return etl::get<TState>(storage);
const TState& get() const
{
return etl::get<TState>(storage);
}
private:
@ -360,20 +361,20 @@ namespace etl
using private_fsm::ifsm_state_helper<>::Pass_To_Parent;
using private_fsm::ifsm_state_helper<>::Self_Transition;
#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above
#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
template <typename, typename, etl::fsm_state_id_t, typename...>
friend class fsm_state;
#else
/*[[[cog
import cog
cog.outl(" template <typename, typename, etl::fsm_state_id_t,")
cog.out(" ")
cog.out(" ")
for n in range(1, int(Handlers)):
cog.out("typename, ")
cog.out(" typename,")
if n % 4 == 0:
cog.outl("")
cog.out(" ")
cog.outl("typename>")
cog.out(" ")
cog.outl(" typename>")
]]]*/
/*[[[end]]]*/
friend class etl::fsm_state;
@ -586,7 +587,7 @@ namespace etl
{
etl::fsm_state_id_t next_state_id = p_state->process_event(message);
process_state_change(next_state_id);
process_state_change(next_state_id);
}
else
{
@ -717,7 +718,7 @@ namespace etl
p_state->on_exit_state();
next_state_id = p_state->on_enter_state();
}
if (have_changed_state(next_state_id))
{
ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id());
@ -748,17 +749,249 @@ namespace etl
};
//*************************************************************************************************
// For C++17 and above.
// For C++11 and above.
//*************************************************************************************************
#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above
#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
//***************************************************************************
// The definition for all types.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
class fsm_state : public ifsm_state
{
private:
using message_id_sequence = etl::index_sequence<TMessageTypes::ID...>;
public:
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");
static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
fsm_state()
: ifsm_state(STATE_ID)
{
}
protected:
~fsm_state()
{
}
TContext& get_fsm_context() const
{
return static_cast<TContext&>(ifsm_state::get_fsm_context());
}
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;
static_assert(Number_Of_Messages > 0, "Zero messages");
//**********************************************
// 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 = etl::fsm_state_id_t (*)(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.
//********************************************
etl::fsm_state_id_t process_event(const etl::imessage& message)
{
const etl::message_id_t id = message.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)
{
const etl::fsm_state_id_t new_state_id = dispatch(message, index);
if (new_state_id != Pass_To_Parent)
{
return new_state_id;
}
}
}
#include "etl/private/diagnostic_array_bounds_push.h"
// If we get here, then we don't have a handler for this message type, so pass it to the parent if we have one, otherwise call on_event_unknown.
return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
#include "etl/private/diagnostic_pop.h"
}
//**********************************************
// Call for a single message type
//**********************************************
template <typename TMessage>
static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg)
{
return derived.on_event(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_event<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.
//**********************************************
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.
//**********************************************
etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index)
{
return 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::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_dispatch_table(etl::make_index_sequence<etl::fsm_state<TContext, TDerived, STATE_ID_, 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::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_id_table(etl::make_index_sequence<etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::Number_Of_Messages>{});
};
#if ETL_USING_CPP11 && !ETL_USING_CPP17
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
constexpr const typename etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table_t
etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table;
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
constexpr const typename etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table_t
etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table;
#endif
/// Definition of STATE_ID
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::STATE_ID;
//***************************************************************************
// The definition for no messages.
//***************************************************************************
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_>
class fsm_state<TContext, TDerived, STATE_ID_> : public ifsm_state
{
private:
using message_id_sequence = etl::index_sequence<>;
public:
using message_types = etl::type_list<>;
using sorted_message_types = etl::type_list<>;
static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
fsm_state()
@ -779,51 +1012,16 @@ namespace etl
private:
//********************************************
struct result_t
{
bool was_handled;
etl::fsm_state_id_t state_id;
};
//********************************************
etl::fsm_state_id_t process_event(const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
const bool was_handled = (process_event_type<TMessageTypes>(message, new_state_id) || ...);
if (!was_handled || (new_state_id == Pass_To_Parent))
{
new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
}
return new_state_id;
}
//********************************************
template <typename TMessage>
bool process_event_type(const etl::imessage& msg, etl::fsm_state_id_t& new_state_id)
{
if (TMessage::ID == msg.get_message_id())
{
new_state_id = static_cast<TDerived*>(this)->on_event(static_cast<const TMessage&>(msg));
return true;
}
else
{
return false;
}
return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
}
};
/// Definition of STATE_ID
template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::STATE_ID;
#else
//*************************************************************************************************
// For C++14 and below.
// For C++03 and below.
//*************************************************************************************************
/*[[[cog
import cog
@ -833,14 +1031,14 @@ namespace etl
cog.outl("//***************************************************************************")
cog.outl("// The definition for all %s message types." % Handlers)
cog.outl("//***************************************************************************")
cog.outl("template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, ")
cog.out(" ")
cog.outl("template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,")
cog.out(" ")
for n in range(1, int(Handlers)):
cog.out("typename T%s = void, " % n)
cog.out(" typename T%s = void," % n)
if n % 4 == 0:
cog.outl("")
cog.out(" ")
cog.outl("typename T%s = void>" % Handlers)
cog.out(" ")
cog.outl(" typename T%s = void>" % Handlers)
cog.outl("class fsm_state : public ifsm_state")
cog.outl("{")
cog.outl("public:")
@ -896,26 +1094,26 @@ namespace etl
else:
cog.outl("// Specialisation for %d message types." % n)
cog.outl("//***************************************************************************")
cog.outl("template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, ")
cog.out(" ")
cog.outl("template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,")
cog.out(" ")
for t in range(1, n):
cog.out("typename T%d, " % t)
cog.out(" typename T%d," % t)
if t % 4 == 0:
cog.outl("")
cog.out(" ")
cog.outl("typename T%d>" % n)
cog.out("class fsm_state<TContext, TDerived, STATE_ID_, ")
cog.out(" ")
cog.outl(" typename T%d>" % n)
cog.out("class fsm_state<TContext, TDerived, STATE_ID_,")
for t in range(1, n + 1):
cog.out("T%d, " % t)
cog.out(" T%d," % t)
if t % 16 == 0:
cog.outl("")
cog.out(" ")
for t in range(n + 1, int(Handlers)):
cog.out("void, ")
cog.out(" void,")
if t % 16 == 0:
cog.outl("")
cog.out(" ")
cog.outl("void> : public ifsm_state")
cog.outl(" void> : public ifsm_state")
cog.outl("{")
cog.outl("public:")
cog.outl("")
@ -966,13 +1164,13 @@ namespace etl
cog.outl("// Specialisation for 0 message types.")
cog.outl("//***************************************************************************")
cog.outl("template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_>")
cog.out("class fsm_state<TContext, TDerived, STATE_ID_, ")
cog.out("class fsm_state<TContext, TDerived, STATE_ID_,")
for t in range(1, int(Handlers)):
cog.out("void, ")
cog.out(" void,")
if t % 16 == 0:
cog.outl("")
cog.out(" ")
cog.outl("void> : public ifsm_state")
cog.outl(" void> : public ifsm_state")
cog.outl("{")
cog.outl("public:")
cog.outl("")
@ -1002,18 +1200,18 @@ namespace etl
cog.outl("};")
cog.outl("")
cog.outl("template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, ")
cog.out(" ")
cog.outl("template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_,")
cog.out(" ")
for n in range(1, int(Handlers)):
cog.out("typename T%s, " % n)
cog.out(" typename T%s," % n)
if n % 4 == 0:
cog.outl("")
cog.out(" ")
cog.outl("typename T%s>" % Handlers)
cog.out("ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, ")
cog.out(" ")
cog.outl(" typename T%s>" % Handlers)
cog.out("ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_,")
for n in range(1, int(Handlers)):
cog.out("T%s, " % n)
cog.outl("T%s>::STATE_ID;" % Handlers)
cog.out(" T%s," % n)
cog.outl(" T%s>::STATE_ID;" % Handlers)
]]]*/
/*[[[end]]]*/
#endif

View File

@ -525,33 +525,31 @@ namespace etl
//***********************************************
void receive(const etl::imessage& msg) ETL_OVERRIDE
{
etl::message_id_t id = msg.get_message_id();
size_t index = Number_Of_Messages;
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)
{
index = get_dispatch_index_from_message_id(id);
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;
}
}
// 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)
// 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())
{
dispatch(msg, index);
get_successor().receive(msg);
}
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);
}
else
{
#include "etl/private/diagnostic_array_bounds_push.h"
static_cast<TDerived*>(this)->on_receive_unknown(msg);
static_cast<TDerived*>(this)->on_receive_unknown(msg);
#include "etl/private/diagnostic_pop.h"
}
}
}
@ -601,29 +599,17 @@ namespace etl
//***********************************************
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);
const size_t index = get_dispatch_index_from_message_id(id);
if (index < Number_Of_Messages)
{
return true;
}
}
if (index < Number_Of_Messages)
{
return true;
}
else
{
if (has_successor())
{
return get_successor().accepts(id);
}
else
{
return false;
}
}
return has_successor() ? get_successor().accepts(id) : false;
}
//********************************************

View File

@ -513,33 +513,31 @@ namespace etl
//***********************************************
void receive(const etl::imessage& msg) ETL_OVERRIDE
{
etl::message_id_t id = msg.get_message_id();
size_t index = Number_Of_Messages;
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)
{
index = get_dispatch_index_from_message_id(id);
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;
}
}
// 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)
// 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())
{
dispatch(msg, index);
get_successor().receive(msg);
}
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);
}
else
{
#include "etl/private/diagnostic_array_bounds_push.h"
static_cast<TDerived*>(this)->on_receive_unknown(msg);
static_cast<TDerived*>(this)->on_receive_unknown(msg);
#include "etl/private/diagnostic_pop.h"
}
}
}
@ -589,29 +587,17 @@ namespace etl
//***********************************************
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);
const size_t index = get_dispatch_index_from_message_id(id);
if (index < Number_Of_Messages)
{
return true;
}
}
if (index < Number_Of_Messages)
{
return true;
}
else
{
if (has_successor())
{
return get_successor().accepts(id);
}
else
{
return false;
}
}
return has_successor() ? get_successor().accepts(id) : false;
}
//********************************************

View File

@ -515,7 +515,7 @@ namespace
//*************************************************************************
TEST(test_fsm_emergency_stop)
{
motorControl.Initialise(stateList, ETL_OR_STD17::size(stateList));
motorControl.Initialise(stateList, ETL_OR_STD17::size(stateList));
motorControl.reset();
motorControl.ClearStatistics();
@ -758,7 +758,7 @@ namespace
CHECK_EQUAL(StateId::Running, int(motorControl.get_state().get_state_id()));
auto id2 = motorControl.transition_to(StateId::Idle);
// Now in Locked state.
CHECK_EQUAL(StateId::Locked, int(id2));
CHECK_EQUAL(StateId::Locked, int(motorControl.get_state_id()));