From 7aeb8636c7ca8fb8e4f921cfde59729ce294f414 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 12 Jun 2017 23:46:51 +0100 Subject: [PATCH 1/5] Experimental versions --- src/CreateFSM.bat | 1 + src/CreateMessageRouter.bat | 1 + src/experimental/fsm.h | 339 ---------- src/fsm.h | 1073 ++++++++++++++++++++++++++++++ src/fsm_generator.h | 477 ++++++++++++++ src/message_router.h | 1132 ++++++++++++++++++++++++++++++++ src/message_router_generator.h | 347 ++++++++++ test/test_fsm.cpp | 497 ++++++++++++++ test/test_message_router.cpp | 528 +++++++++++++++ 9 files changed, 4056 insertions(+), 339 deletions(-) create mode 100644 src/CreateFSM.bat create mode 100644 src/CreateMessageRouter.bat delete mode 100644 src/experimental/fsm.h create mode 100644 src/fsm.h create mode 100644 src/fsm_generator.h create mode 100644 src/message_router.h create mode 100644 src/message_router_generator.h create mode 100644 test/test_fsm.cpp create mode 100644 test/test_message_router.cpp diff --git a/src/CreateFSM.bat b/src/CreateFSM.bat new file mode 100644 index 00000000..1cb113c4 --- /dev/null +++ b/src/CreateFSM.bat @@ -0,0 +1 @@ +python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h \ No newline at end of file diff --git a/src/CreateMessageRouter.bat b/src/CreateMessageRouter.bat new file mode 100644 index 00000000..710d838d --- /dev/null +++ b/src/CreateMessageRouter.bat @@ -0,0 +1 @@ +python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h \ No newline at end of file diff --git a/src/experimental/fsm.h b/src/experimental/fsm.h deleted file mode 100644 index 6c65c20a..00000000 --- a/src/experimental/fsm.h +++ /dev/null @@ -1,339 +0,0 @@ - -#ifndef __ETL_FSM__ -#define __ETL_FSM__ - -#include - -#include "../array.h" -#include "../nullptr.h" -#include "../error_handler.h" -#include "../exception.h" - -#undef ETL_FILE -#define ETL_FILE "34" - -namespace etl -{ -#if !defined(ETL_FSM_STATE_ID_TYPE) - typedef uint_least8_t fsm_state_id_t; -#else - typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t; -#endif - -#if !defined(ETL_FSM_EVENT_ID_TYPE) - typedef uint_least8_t fsm_event_id_t; -#else - typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t; -#endif - - //*************************************************************************** - /// Base exception class for FSM. - //*************************************************************************** - class fsm_exception : public etl::exception - { - public: - - fsm_exception(string_type what, string_type file_name, numeric_type line_number) - : etl::exception(what, file_name, line_number) - { - } - }; - - //*************************************************************************** - /// Exception for null state pointer. - //*************************************************************************** - class fsm_nullstate_exception : public etl::fsm_exception - { - public: - - fsm_nullstate_exception(string_type file_name, numeric_type line_number) - : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number) - { - } - }; - - //*************************************************************************** - /// Exception for invalid state id. - //*************************************************************************** - class fsm_state_id_exception : public etl::fsm_exception - { - public: - - fsm_state_id_exception(string_type file_name, numeric_type line_number) - : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number) - { - } - }; - - //*************************************************************************** - /// Interface class for FSM events. - //*************************************************************************** - class ifsm_event - { - public: - - //******************************************* - /// Gets the id for this event. - //******************************************* - etl::fsm_event_id_t get_event_id() const - { - return event_id; - } - - protected: - - //******************************************* - /// Constructor. - //******************************************* - ifsm_event(etl::fsm_event_id_t event_id_) - : event_id(event_id_) - { - } - - private: - - // The event id. - const etl::fsm_event_id_t event_id; - }; - - //*************************************************************************** - /// Base class for FSM events. - //*************************************************************************** - template - class fsm_event : public etl::ifsm_event - { - public: - - enum - { - EVENT_ID = EVENT_ID_ - }; - - //******************************************* - /// Constructor. - //******************************************* - fsm_event() - : ifsm_event(EVENT_ID) - { - } - }; - - //*************************************************************************** - /// Interface class for FSM states. - //*************************************************************************** - class ifsm_state - { - public: - - //******************************************* - /// Gets the id for this state. - //******************************************* - etl::fsm_state_id_t get_state_id() const - { - return state_id; - } - - virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0; - - protected: - - //******************************************* - /// Constructor. - //******************************************* - ifsm_state(etl::fsm_state_id_t state_id_) - : state_id(state_id_) - { - } - - virtual void on_enter_state() {}; // By default, do nothing. - virtual void on_exit_state() {}; // By default, do nothing. - - private: - - // The state id. - const etl::fsm_state_id_t state_id; - - // Disabled. - ifsm_state(const ifsm_state&); - ifsm_state& operator =(const ifsm_state&); - }; - - //*************************************************************************** - // To be COG generated. - /// Base class for FSM states. - //*************************************************************************** - template - class fsm_state : public ifsm_state - { - public: - - enum - { - STATE_ID = STATE_ID_ - }; - - //******************************************* - /// Constructor. - //******************************************* - fsm_state() - : ifsm_state(STATE_ID) - { - } - - //******************************************* - /// Top level event handler for the state. - //******************************************* - etl::fsm_state_id_t on_event(const etl::ifsm_event& event) - { - etl::fsm_state_id_t new_state_id; - etl::fsm_event_id_t id = event.get_event_id(); - - switch (id) - { - case T1::EVENT_ID: new_state_id = static_cast(*this).on_event(static_cast(event)); break; - case T2::EVENT_ID: new_state_id = static_cast(*this).on_event(static_cast(event)); break; - default: new_state_id = static_cast(*this).on_unknown_event(event); break; - } - - return new_state_id; - } - - private: - - // Disabled. - fsm_state(const fsm_state&); - fsm_state& operator =(const fsm_state&); - }; - - //*************************************************************************** - template - class fsm - { - public: - - //******************************************* - /// Constructor. - //******************************************* - fsm() - : p_state(nullptr) - { - state_list.fill(nullptr); - } - - //******************************************* - /// Starts the FSM. - /// Can only be called once. - /// Subsequent calls will do nothing. - //******************************************* - void start() - { - // Can only be started once. - if (p_state == nullptr) - { - p_state = state_list[0]; - ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_nullstate_exception)); - - p_state->on_enter_state(); - } - } - - //******************************************* - /// Adds a state to the FSM. - /// If the state has the same id as one already added - /// then the current state will be overwritten. - //******************************************* - void add_state(etl::ifsm_state& state) - { - ETL_ASSERT(state.get_state_id() < MAX_STATES, ETL_ERROR(etl::fsm_state_id_exception)); - state_list[state.get_state_id()] = &state; - } - - //******************************************* - /// Top level event handler for the FSM. - //******************************************* - void on_event(const etl::ifsm_event& event) - { - etl::fsm_state_id_t next_state_id = p_state->on_event(event); - ETL_ASSERT(next_state_id < MAX_STATES, ETL_ERROR(etl::fsm_state_id_exception)); - - // Have we changed state? - if (next_state_id != p_state->get_state_id()) - { - p_state->on_exit_state(); - - p_state = state_list[next_state_id]; - ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_nullstate_exception)); - - p_state->on_enter_state(); - } - } - - //******************************************* - /// Gets the current state id. - //******************************************* - etl::fsm_state_id_t get_state_id() const - { - return p_state->get_state_id(); - } - - //******************************************* - /// Gets a reference to the current state interface. - //******************************************* - ifsm_state& get_state() - { - return *p_state; - } - - //******************************************* - /// Gets a const reference to the current state interface. - //******************************************* - const ifsm_state& get_state() const - { - return *p_state; - } - - private: - - etl::ifsm_state* p_state; ///< A pointer to the current state. - etl::array state_list; ///< The list of added states. - - // Disabled. - fsm(const fsm&); - fsm& operator =(const fsm&); - }; -} - -#undef ETL_FILE - -// class Start : public etl::fsm_event<0> -// { - -// }; - -// class Idle : public etl::fsm_state -// { -// public: - -// etl::fsm_state_id_t on_event(const Start& event) -// { -// return 1; -// } - -// etl::fsm_state_id_t on_unknown_event(const etl::ifsm_event& event) -// { -// return THIS_STATE_ID; -// } -// }; - -// etl::fsm<3> machine; - -// void F() -// { -// Idle idle; - -// machine.add_state(idle); -// machine.start(); -// machine.on_event(Start()); -// } - -#endif \ No newline at end of file diff --git a/src/fsm.h b/src/fsm.h new file mode 100644 index 00000000..9b049dde --- /dev/null +++ b/src/fsm.h @@ -0,0 +1,1073 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +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_FSM__ +#define __ETL_FSM__ + +#include + +#include "array.h" +#include "nullptr.h" +#include "error_handler.h" +#include "exception.h" +#include "user_type.h" + +#undef ETL_FILE +#define ETL_FILE "34" + +namespace etl +{ +#if !defined(ETL_FSM_STATE_ID_TYPE) + typedef uint_least8_t fsm_state_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t; +#endif + +#if !defined(ETL_FSM_EVENT_ID_TYPE) + typedef uint_least8_t fsm_event_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t; +#endif + + //*************************************************************************** + /// Base exception class for FSM. + //*************************************************************************** + class fsm_exception : public etl::exception + { + public: + + fsm_exception(string_type what, string_type file_name, numeric_type line_number) + : etl::exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for null state pointer. + //*************************************************************************** + class fsm_null_state_exception : public etl::fsm_exception + { + public: + + fsm_null_state_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for invalid state id. + //*************************************************************************** + class fsm_state_id_exception : public etl::fsm_exception + { + public: + + fsm_state_id_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for incompatible state list. + //*************************************************************************** + class fsm_state_list_exception : public etl::fsm_exception + { + public: + + fsm_state_list_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list", ETL_FILE"C"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM events. + //*************************************************************************** + class ifsm_event + { + public: + + //******************************************* + /// Gets the id for this event. + //******************************************* + etl::fsm_event_id_t get_event_id() const + { + return event_id; + } + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_event(etl::fsm_event_id_t event_id_) + : event_id(event_id_) + { + } + + private: + + // The event id. + const etl::fsm_event_id_t event_id; + }; + + //*************************************************************************** + /// Base class for FSM events. + //*************************************************************************** + template + class fsm_event : public etl::ifsm_event + { + public: + + enum + { + EVENT_ID = EVENT_ID_ + }; + + //******************************************* + /// Constructor. + //******************************************* + fsm_event() + : ifsm_event(EVENT_ID) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM states. + //*************************************************************************** + class ifsm_state + { + public: + + friend class fsm; + + //******************************************* + /// Gets the id for this state. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + return state_id; + } + + virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0; + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_state(etl::fsm_state_id_t state_id_) + : state_id(state_id_) + { + } + + virtual void on_enter_state() {}; // By default, do nothing. + virtual void on_exit_state() {}; // By default, do nothing. + + private: + + // The state id. + const etl::fsm_state_id_t state_id; + + // Disabled. + ifsm_state(const ifsm_state&); + ifsm_state& operator =(const ifsm_state&); + }; + + //*************************************************************************** + class fsm + { + public: + + //******************************************* + /// Constructor. + //******************************************* + fsm() + : p_state(nullptr) + { + } + + //******************************************* + /// Set the states for the FSM + //******************************************* + template + void set_states(etl::ifsm_state** p_states, TSize size) + { + state_list = p_states; + number_of_states = etl::fsm_state_id_t(size); + + for (etl::fsm_state_id_t i = 0; i < size; ++i) + { + ETL_ASSERT((state_list[i] != nullptr), ETL_ERROR(etl::fsm_null_state_exception)); + } + + bool ok = (number_of_states > 0) && + etl::is_sorted(state_list, state_list + number_of_states, fsm::CompareStateId()); + + ETL_ASSERT(ok, ETL_ERROR(etl::fsm_state_list_exception)); + } + + //******************************************* + /// Starts the FSM. + /// Can only be called once. + /// Subsequent calls will do nothing. + //******************************************* + void start() + { + // Can only be started once. + if (p_state == nullptr) + { + p_state = state_list[0]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Top level event handler for the FSM. + //******************************************* + void on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t next_state_id = p_state->on_event(event); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + + // Have we changed state? + if (next_state_id != p_state->get_state_id()) + { + p_state->on_exit_state(); + + p_state = state_list[next_state_id]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Gets the current state id. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return p_state->get_state_id(); + } + + //******************************************* + /// Gets a reference to the current state interface. + //******************************************* + ifsm_state& get_state() + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Gets a const reference to the current state interface. + //******************************************* + const ifsm_state& get_state() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Checks if the FSM has been started. + //******************************************* + bool is_started() const + { + return p_state != nullptr; + } + + //******************************************* + /// Reset the FSM to pre-started state. + //******************************************* + void reset() + { + p_state = nullptr; + } + + private: + + struct CompareStateId + { + bool operator()(etl::ifsm_state* lhs, etl::ifsm_state* rhs) + { + return lhs->get_state_id() < rhs->get_state_id(); + } + }; + + etl::ifsm_state* p_state; ///< A pointer to the current state. + etl::ifsm_state** state_list; ///< The list of added states. + etl::fsm_state_id_t number_of_states; ///< The number of states. + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -ofsm.h -DHandlers= fsm_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 events... + // python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h + // + // See CreateFSM.bat + //*************************************************************************** + + //*************************************************************************** + // The code below has been auto generated. Do not manually edit. + //*************************************************************************** + + //*************************************************************************** + // The definition for all 16 message types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + + friend class fsm; + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T14::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T15::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T16::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 15 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T14::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T15::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 14 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T14::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 13 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 12 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 11 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 10 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 9 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 8 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 7 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 6 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 5 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 4 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 3 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 2 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 1 event type. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; +} + +#undef ETL_FILE + +#endif \ No newline at end of file diff --git a/src/fsm_generator.h b/src/fsm_generator.h new file mode 100644 index 00000000..4888e91a --- /dev/null +++ b/src/fsm_generator.h @@ -0,0 +1,477 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +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_FSM__ +#define __ETL_FSM__ + +#include + +#include "array.h" +#include "nullptr.h" +#include "error_handler.h" +#include "exception.h" +#include "user_type.h" + +#undef ETL_FILE +#define ETL_FILE "34" + +namespace etl +{ +#if !defined(ETL_FSM_STATE_ID_TYPE) + typedef uint_least8_t fsm_state_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t; +#endif + +#if !defined(ETL_FSM_EVENT_ID_TYPE) + typedef uint_least8_t fsm_event_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t; +#endif + + //*************************************************************************** + /// Base exception class for FSM. + //*************************************************************************** + class fsm_exception : public etl::exception + { + public: + + fsm_exception(string_type what, string_type file_name, numeric_type line_number) + : etl::exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for null state pointer. + //*************************************************************************** + class fsm_null_state_exception : public etl::fsm_exception + { + public: + + fsm_null_state_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for invalid state id. + //*************************************************************************** + class fsm_state_id_exception : public etl::fsm_exception + { + public: + + fsm_state_id_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for incompatible state list. + //*************************************************************************** + class fsm_state_list_exception : public etl::fsm_exception + { + public: + + fsm_state_list_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list", ETL_FILE"C"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM events. + //*************************************************************************** + class ifsm_event + { + public: + + //******************************************* + /// Gets the id for this event. + //******************************************* + etl::fsm_event_id_t get_event_id() const + { + return event_id; + } + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_event(etl::fsm_event_id_t event_id_) + : event_id(event_id_) + { + } + + private: + + // The event id. + const etl::fsm_event_id_t event_id; + }; + + //*************************************************************************** + /// Base class for FSM events. + //*************************************************************************** + template + class fsm_event : public etl::ifsm_event + { + public: + + enum + { + EVENT_ID = EVENT_ID_ + }; + + //******************************************* + /// Constructor. + //******************************************* + fsm_event() + : ifsm_event(EVENT_ID) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM states. + //*************************************************************************** + class ifsm_state + { + public: + + friend class fsm; + + //******************************************* + /// Gets the id for this state. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + return state_id; + } + + virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0; + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_state(etl::fsm_state_id_t state_id_) + : state_id(state_id_) + { + } + + virtual void on_enter_state() {}; // By default, do nothing. + virtual void on_exit_state() {}; // By default, do nothing. + + private: + + // The state id. + const etl::fsm_state_id_t state_id; + + // Disabled. + ifsm_state(const ifsm_state&); + ifsm_state& operator =(const ifsm_state&); + }; + + //*************************************************************************** + class fsm + { + public: + + //******************************************* + /// Constructor. + //******************************************* + fsm() + : p_state(nullptr) + { + } + + //******************************************* + /// Set the states for the FSM + //******************************************* + template + void set_states(etl::ifsm_state** p_states, TSize size) + { + state_list = p_states; + number_of_states = etl::fsm_state_id_t(size); + + for (etl::fsm_state_id_t i = 0; i < size; ++i) + { + ETL_ASSERT((state_list[i] != nullptr), ETL_ERROR(etl::fsm_null_state_exception)); + } + + bool ok = (number_of_states > 0) && + etl::is_sorted(state_list, state_list + number_of_states, fsm::CompareStateId()); + + ETL_ASSERT(ok, ETL_ERROR(etl::fsm_state_list_exception)); + } + + //******************************************* + /// Starts the FSM. + /// Can only be called once. + /// Subsequent calls will do nothing. + //******************************************* + void start() + { + // Can only be started once. + if (p_state == nullptr) + { + p_state = state_list[0]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Top level event handler for the FSM. + //******************************************* + void on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t next_state_id = p_state->on_event(event); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + + // Have we changed state? + if (next_state_id != p_state->get_state_id()) + { + p_state->on_exit_state(); + + p_state = state_list[next_state_id]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Gets the current state id. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return p_state->get_state_id(); + } + + //******************************************* + /// Gets a reference to the current state interface. + //******************************************* + ifsm_state& get_state() + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Gets a const reference to the current state interface. + //******************************************* + const ifsm_state& get_state() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Checks if the FSM has been started. + //******************************************* + bool is_started() const + { + return p_state != nullptr; + } + + //******************************************* + /// Reset the FSM to pre-started state. + //******************************************* + void reset() + { + p_state = nullptr; + } + + private: + + struct CompareStateId + { + bool operator()(etl::ifsm_state* lhs, etl::ifsm_state* rhs) + { + return lhs->get_state_id() < rhs->get_state_id(); + } + }; + + etl::ifsm_state* p_state; ///< A pointer to the current state. + etl::ifsm_state** state_list; ///< The list of added states. + etl::fsm_state_id_t number_of_states; ///< The number of states. + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -ofsm.h -DHandlers= fsm_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 events... + // python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h + // + // See CreateFSM.bat + //*************************************************************************** + + /*[[[cog + import cog + cog.outl("//***************************************************************************") + cog.outl("// The code below has been auto generated. Do not manually edit.") + cog.outl("//***************************************************************************") + cog.outl("") + ################################################ + # The first definition for all of the events. + ################################################ + cog.outl("//***************************************************************************") + cog.outl("// The definition for all %s message types." % Handlers) + cog.outl("//***************************************************************************") + cog.outl("template " % Handlers) + cog.outl("class fsm_state : public ifsm_state") + cog.outl("{") + cog.outl("public:") + cog.outl("") + + cog.outl(" friend class fsm;") + cog.outl("") + + cog.outl(" enum") + cog.outl(" {") + cog.outl(" STATE_ID = STATE_ID_") + cog.outl(" };") + cog.outl("") + cog.outl(" fsm_state()") + cog.outl(" : ifsm_state(STATE_ID)") + cog.outl(" {") + cog.outl(" }") + cog.outl("") + cog.outl(" etl::fsm_state_id_t on_event(const etl::ifsm_event& event)") + cog.outl(" {") + cog.outl(" etl::fsm_state_id_t new_state_id;") + cog.outl(" etl::fsm_event_id_t event_id = event.get_event_id();") + cog.outl("") + cog.outl(" switch (event_id)") + cog.outl(" {") + for n in range(1, int(Handlers) + 1): + cog.out(" case T%d::EVENT_ID:" % n) + cog.out(" new_state_id = static_cast(this)->on_event(static_cast(event));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" new_state_id = static_cast(this)->on_event_unknown(event);") + cog.outl(" break;") + cog.outl(" }") + cog.outl("") + cog.outl(" return new_state_id;") + cog.outl(" }") + cog.outl("};") + + #################################### + # All of the other specialisations. + #################################### + for n in range(int(Handlers) - 1, 0, -1): + cog.outl("") + cog.outl("//***************************************************************************") + if n == 1: + cog.outl("// Specialisation for %d event type." % n) + else: + cog.outl("// Specialisation for %d event types." % n) + cog.outl("//***************************************************************************") + cog.outl("template " % n) + cog.out("class fsm_state : public ifsm_state") + cog.outl("{") + cog.outl("public:") + + cog.outl(" friend class fsm;") + cog.outl("") + + cog.outl("") + cog.outl(" enum") + cog.outl(" {") + cog.outl(" STATE_ID = STATE_ID_") + cog.outl(" };") + cog.outl("") + cog.outl(" fsm_state()") + cog.outl(" : ifsm_state(STATE_ID)") + cog.outl(" {") + cog.outl(" }") + cog.outl("") + cog.outl(" etl::fsm_state_id_t on_event(const etl::ifsm_event& event)") + cog.outl(" {") + cog.outl(" etl::fsm_state_id_t new_state_id;") + cog.outl(" etl::fsm_event_id_t event_id = event.get_event_id();") + cog.outl("") + cog.outl(" switch (event_id)") + cog.outl(" {") + for n in range(1, n + 1): + cog.out(" case T%d::EVENT_ID:" % n) + cog.out(" new_state_id = static_cast(this)->on_event(static_cast(event));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" new_state_id = static_cast(this)->on_event_unknown(event);") + cog.outl(" break;") + cog.outl(" }") + cog.outl("") + cog.outl(" return new_state_id;") + cog.outl(" }") + cog.outl("};") + ]]]*/ + /*[[[end]]]*/ +} + +#undef ETL_FILE + +#endif \ No newline at end of file diff --git a/src/message_router.h b/src/message_router.h new file mode 100644 index 00000000..7911e04d --- /dev/null +++ b/src/message_router.h @@ -0,0 +1,1132 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +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__ +#define __ETL_MESSAGE_ROUTER__ + +#include + +namespace etl +{ + //*************************************************************************** + class imessage + { + public: + + typedef size_t id_t; + + virtual ~imessage() {} + virtual id_t get_message_id() const = 0; + }; + + //*************************************************************************** + template + class message : public imessage + { + public: + + enum + { + ID = ID_ + }; + + id_t get_message_id() const + { + return ID; + } + }; + + //*************************************************************************** + template + class imessage_router + { + public: + + typedef TReturn return_type; + + virtual ~imessage_router() {} + virtual TReturn receive(imessage_router& source, const imessage& message) = 0; + + TReturn send_to(imessage_router& destination, const imessage& message) + { + return destination.receive(*this, message); + } + }; + + //*************************************************************************** + template <> + class imessage_router + { + public: + + typedef void return_type; + + virtual ~imessage_router() {} + virtual void receive(imessage_router& source, const imessage& message) = 0; + + void send_to(imessage_router& destination, const imessage& message) + { + destination.receive(*this, message); + } + }; + + //*************************************************************************** + template + class null_message_router : public imessage_router + { + public: + + TReturn receive(imessage_router& source, const imessage& message) + { + return TReturn(); + } + }; + + //*************************************************************************** + template <> + class null_message_router : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& message) + { + } + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -omessage_router.h -DHandlers= message_router_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 messages... + // python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h + // + // See CreateMessageProcessor.bat + //*************************************************************************** + + //*************************************************************************** + // The code below has been auto generated. Do not manually edit. + //*************************************************************************** + + //*************************************************************************** + // The definition for all 16 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + TReturn receive(imessage_router& source, const imessage& msg) + { + const id_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T16::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // The definition for all 16 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const id_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T16::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 15 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 15 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 14 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 14 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 13 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 13 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 12 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 12 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 11 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 11 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 10 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 10 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 9 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 9 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 8 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 8 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 7 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 7 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 6 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 6 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 5 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 5 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 4 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 4 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 3 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 3 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 2 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 2 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 1 message type, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 1 message type, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; +} + +#endif diff --git a/src/message_router_generator.h b/src/message_router_generator.h new file mode 100644 index 00000000..57879607 --- /dev/null +++ b/src/message_router_generator.h @@ -0,0 +1,347 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +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__ +#define __ETL_MESSAGE_ROUTER__ + +#include + +namespace etl +{ + //*************************************************************************** + class imessage + { + public: + + typedef size_t id_t; + + virtual ~imessage() {} + virtual id_t get_message_id() const = 0; + }; + + //*************************************************************************** + template + class message : public imessage + { + public: + + enum + { + ID = ID_ + }; + + id_t get_message_id() const + { + return ID; + } + }; + + //*************************************************************************** + template + class imessage_router + { + public: + + typedef TReturn return_type; + + virtual ~imessage_router() {} + virtual TReturn receive(imessage_router& source, const imessage& message) = 0; + + TReturn send_to(imessage_router& destination, const imessage& message) + { + return destination.receive(*this, message); + } + }; + + //*************************************************************************** + template <> + class imessage_router + { + public: + + typedef void return_type; + + virtual ~imessage_router() {} + virtual void receive(imessage_router& source, const imessage& message) = 0; + + void send_to(imessage_router& destination, const imessage& message) + { + destination.receive(*this, message); + } + }; + + //*************************************************************************** + template + class null_message_router : public imessage_router + { + public: + + null_message_router() + : value(TReturn()) + { + } + + null_message_router(const TReturn& value) + : value(value) + { + } + + null_message_router(const null_message_router& other) + : value(other.value) + { + } + + null_message_router& operator ==(const null_message_router& other) + : value(other.value) + { + } + + TReturn receive(imessage_router& source, const imessage& message) + { + return value; + } + + private: + + TReturn value; + }; + + //*************************************************************************** + template <> + class null_message_router : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& message) + { + } + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -omessage_router.h -DHandlers= message_router_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 messages... + // python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h + // + // See CreateMessageProcessor.bat + //*************************************************************************** + + /*[[[cog + import cog + cog.outl("//***************************************************************************") + cog.outl("// The code below has been auto generated. Do not manually edit.") + cog.outl("//***************************************************************************") + cog.outl("") + ################################################ + # The first definition for all of the messages. + ################################################ + cog.outl("//***************************************************************************") + cog.outl("// The definition for all %s message types, returning TReturn." % Handlers) + cog.outl("//***************************************************************************") + cog.outl("template " % Handlers) + cog.outl("class message_router") + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" TReturn receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const id_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for n in range(1, int(Handlers) + 1): + cog.out(" case T%d::ID:" % n) + cog.out(" return static_cast(this)->on_receive(source, static_cast(msg));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" return static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + cog.outl("") + + ################################################ + # The first definition for all of the messages, with void return type. + ################################################ + cog.outl("//***************************************************************************") + cog.outl("// The definition for all %s message types, returning void." % Handlers) + cog.outl("//***************************************************************************") + cog.outl("template " % Handlers) + cog.out("class message_router" % Handlers) + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" void receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const id_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for n in range(1, int(Handlers) + 1): + cog.out(" case T%d::ID:" % n) + cog.out(" static_cast(this)->on_receive(source, static_cast(msg));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + + #################################### + # All of the other specialisations. + #################################### + for n in range(int(Handlers) - 1, 0, -1): + cog.outl("") + cog.outl("//***************************************************************************") + if n == 1: + cog.outl("// Specialisation for %d message type, returning TReturn." % n) + else: + cog.outl("// Specialisation for %d message types, returning TReturn." % n) + cog.outl("//***************************************************************************") + cog.outl("template " % n) + cog.out("class message_router") + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" return_type receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const size_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for t in range(1, n + 1): + cog.out(" case T%d::ID:" % t) + cog.out(" return static_cast(this)->on_receive(source, static_cast(msg));" % t) + cog.outl(" break;") + cog.out(" default:") + cog.out(" return static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + + cog.outl("") + cog.outl("//***************************************************************************") + if n == 1: + cog.outl("// Specialisation for %d message type, returning void." % n) + else: + cog.outl("// Specialisation for %d message types, returning void." % n) + cog.outl("//***************************************************************************") + cog.outl("template " % n) + cog.out("class message_router") + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" void receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const size_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for t in range(1, n + 1): + cog.out(" case T%d::ID:" % t) + cog.out(" static_cast(this)->on_receive(source, static_cast(msg));" % t) + cog.outl(" break;") + cog.out(" default:") + cog.out(" static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + ]]]*/ + /*[[[end]]]*/ +} + +#endif diff --git a/test/test_fsm.cpp b/test/test_fsm.cpp new file mode 100644 index 00000000..edcd1678 --- /dev/null +++ b/test/test_fsm.cpp @@ -0,0 +1,497 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +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. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "fsm.h" +#include "enum_type.h" +#include "container.h" + +#include + +namespace +{ + //*************************************************************************** + // Events + struct EventId + { + enum enum_type + { + START, + STOP, + STOPPED, + SET_SPEED + }; + + ETL_DECLARE_ENUM_TYPE(EventId, etl::fsm_event_id_t) + ETL_ENUM_TYPE(START, "Start") + ETL_ENUM_TYPE(STOP, "Stop") + ETL_ENUM_TYPE(STOPPED, "Stopped") + ETL_ENUM_TYPE(SET_SPEED, "Set Speed") + ETL_END_ENUM_TYPE + }; + + //*********************************** + class Start : public etl::fsm_event + { + }; + + //*********************************** + class Stop : public etl::fsm_event + { + public: + + Stop() : isEmergencyStop(false) {} + Stop(bool emergency) : isEmergencyStop(emergency) {} + + const bool isEmergencyStop; + }; + + //*********************************** + class SetSpeed : public etl::fsm_event + { + public: + + SetSpeed(int speed) : speed(speed) {} + + const int speed; + }; + + //*********************************** + class Stopped : public etl::fsm_event + { + }; + + //*************************************************************************** + // States + struct StateId + { + enum enum_type + { + IDLE, + RUNNING, + WINDING_DOWN, + NUMBER_OF_STATES + }; + + ETL_DECLARE_ENUM_TYPE(StateId, etl::fsm_state_id_t) + ETL_ENUM_TYPE(IDLE, "Idle") + ETL_ENUM_TYPE(RUNNING, "Running") + ETL_ENUM_TYPE(WINDING_DOWN, "Winding Down") + ETL_END_ENUM_TYPE + }; + + class MotorControl; + + //*********************************** + // Common functionality + //*********************************** + class Common + { + public: + + //*********************************** + Common() + { + ClearStatistics(); + } + + //*********************************** + void ClearStatistics() + { + startCount = 0; + stopCount = 0; + setSpeedCount = 0; + unknownCount = 0; + stoppedCount = 0; + isLampOn = false; + speed = 0; + } + + //*********************************** + void SetSpeed(int speed_) + { + speed = speed_; + } + + //*********************************** + void TurnRunningLampOn() + { + isLampOn = true; + } + + //*********************************** + void TurnRunningLampOff() + { + isLampOn = false; + } + + int startCount; + int stopCount; + int setSpeedCount; + int unknownCount; + int stoppedCount; + bool isLampOn; + int speed; + }; + + //*********************************** + // The idle state. + //*********************************** + class Idle : public etl::fsm_state + { + public: + + //*********************************** + Idle(Common& common) + : common(common) + { + } + + //*********************************** + etl::fsm_state_id_t on_event(const Start& event) + { + ++common.startCount; + return StateId::RUNNING; + } + + //*********************************** + etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event) + { + ++common.unknownCount; + return STATE_ID; + } + + //*********************************** + void on_enter_state() + { + common.TurnRunningLampOff(); + } + + Common& common; + }; + + //*********************************** + // The running state. + //*********************************** + class Running : public etl::fsm_state + { + public: + + //*********************************** + Running(Common& common) + : common(common) + { + } + + //*********************************** + etl::fsm_state_id_t on_event(const Stop& event) + { + ++common.stopCount; + + if (event.isEmergencyStop) + { + return StateId::IDLE; + } + else + { + return StateId::WINDING_DOWN; + } + } + + //*********************************** + etl::fsm_state_id_t on_event(const SetSpeed& event) + { + ++common.setSpeedCount; + common.SetSpeed(event.speed); + return STATE_ID; + } + + //*********************************** + etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event) + { + ++common.unknownCount; + return STATE_ID; + } + + void on_enter_state() + { + common.TurnRunningLampOn(); + } + + Common& common; + }; + + //*********************************** + // The winding down state. + //*********************************** + class WindingDown : public etl::fsm_state + { + public: + + //*********************************** + WindingDown(Common& common) + : common(common) + { + } + + //*********************************** + etl::fsm_state_id_t on_event(const Stopped& event) + { + ++common.stoppedCount; + return StateId::IDLE; + } + + //*********************************** + etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event) + { + ++common.unknownCount; + return STATE_ID; + } + + Common& common; + }; + + //*********************************** + // The motor control FSM. + //*********************************** + class MotorControl : public etl::fsm + { + public: + + MotorControl() + : idle(common), + running(common), + windingDown(common) + { + set_states(stateList, etl::size(stateList)); + } + + Common common; + + private: + + Idle idle; + Running running; + WindingDown windingDown; + + etl::ifsm_state* stateList[StateId::NUMBER_OF_STATES] = + { + &idle, &running, &windingDown + }; + }; + + MotorControl motorControl; + + SUITE(test_map) + { + //************************************************************************* + TEST(test_fsm) + { + motorControl.reset(); + motorControl.common.ClearStatistics(); + + CHECK(!motorControl.is_started()); + + // Start the FSM. + motorControl.start(); + CHECK(motorControl.is_started()); + + // Now in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(0, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(0, motorControl.common.unknownCount); + + // Send unhandled events. + motorControl.on_event(Stop()); + motorControl.on_event(Stopped()); + motorControl.on_event(SetSpeed(10)); + + CHECK_EQUAL(StateId::IDLE, motorControl.get_state_id()); + CHECK_EQUAL(StateId::IDLE, motorControl.get_state().get_state_id()); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(0, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(3, motorControl.common.unknownCount); + + // Send Start event. + motorControl.on_event(Start()); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(3, motorControl.common.unknownCount); + + // Send unhandled events. + motorControl.on_event(Start()); + motorControl.on_event(Stopped()); + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(5, motorControl.common.unknownCount); + + // Send SetSpeed event. + motorControl.on_event(SetSpeed(100)); + + // Still in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(5, motorControl.common.unknownCount); + + // Send Stop event. + motorControl.on_event(Stop()); + + // Now in WindingDown state. + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(5, motorControl.common.unknownCount); + + // Send unhandled events. + motorControl.on_event(Start()); + motorControl.on_event(Stop()); + motorControl.on_event(SetSpeed(100)); + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(8, motorControl.common.unknownCount); + + // Send Stopped event. + motorControl.on_event(Stopped()); + + // Now in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(1, motorControl.common.stoppedCount); + CHECK_EQUAL(8, motorControl.common.unknownCount); + } + + //************************************************************************* + TEST(test_fsm_emergency_stop) + { + motorControl.reset(); + motorControl.common.ClearStatistics(); + + CHECK(!motorControl.is_started()); + + // Start the FSM. + motorControl.start(); + CHECK(motorControl.is_started()); + + // Now in Idle state. + + // Send Start event. + motorControl.on_event(Start()); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(0, motorControl.common.unknownCount); + + // Send emergency Stop event. + motorControl.on_event(Stop(true)); + + // Now in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(0, motorControl.common.unknownCount); + } + }; +} diff --git a/test/test_message_router.cpp b/test/test_message_router.cpp new file mode 100644 index 00000000..b8620502 --- /dev/null +++ b/test/test_message_router.cpp @@ -0,0 +1,528 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +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. +******************************************************************************/ + +#include "UnitTest++.h" +#include "ExtraCheckMacros.h" + +#include "message_router.h" + +//*************************************************************************** +// The set of messages. +//*************************************************************************** +namespace +{ + enum + { + MESSAGE1, + MESSAGE2, + MESSAGE3, + MESSAGE4, + MESSAGE5 + }; + + struct Message1 : public etl::message + { + + }; + + struct Message2 : public etl::message + { + + }; + + struct Message3 : public etl::message + { + + }; + + struct Message4 : public etl::message + { + + }; + + struct Message5 : public etl::message + { + + }; + + Message1 message1; + Message2 message2; + Message3 message3; + Message4 message4; + Message5 message5; + + // Router typedefs. + typedef etl::imessage_router int_router_t; + typedef etl::imessage_router void_router_t; + + //*************************************************************************** + // Router that handles messages 1, 2, 3, 4 and 5 and returns nothing. + //*************************************************************************** + class RouterVoid1 : public etl::message_router + { + public: + + RouterVoid1() + : message1_count(0), + message2_count(0), + message3_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + void on_receive(void_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message3& msg) + { + ++message3_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message5& msg) + { + ++callback_count; + } + + void on_receive_unknown(void_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + } + + int message1_count; + int message2_count; + int message3_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + //*************************************************************************** + // Router that handles messages 1, 2, 4 and 5 and returns nothing. + //*************************************************************************** + class RouterVoid2 : public etl::message_router + { + public: + + RouterVoid2() + : message1_count(0), + message2_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + void on_receive(void_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message5& msg) + { + ++callback_count; + } + + void on_receive_unknown(void_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + send_to(sender, message5); + } + + int message1_count; + int message2_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + //*************************************************************************** + // Router that handles messages 1, 2, 3, 4 and 5 and returns an int. + //*************************************************************************** + class RouterInt1 : public etl::message_router + { + public: + + RouterInt1() + : message1_count(0), + message2_count(0), + message3_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + int on_receive(int_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message3& msg) + { + ++message3_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message5& msg) + { + ++callback_count; + return msg.ID; + } + + int on_receive_unknown(int_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + return msg.get_message_id(); + } + + int message1_count; + int message2_count; + int message3_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + //*************************************************************************** + // Router that handles messages 1, 2, 4 and 5 and returns an int. + //*************************************************************************** + class RouterInt2 : public etl::message_router + { + public: + + RouterInt2() + : message1_count(0), + message2_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + int on_receive(int_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message5& msg) + { + ++callback_count; + return msg.ID; + } + + int on_receive_unknown(int_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + send_to(sender, message5); + return msg.get_message_id(); + } + + int message1_count; + int message2_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + // Null router types. + typedef etl::null_message_router int_null_router_t; + typedef etl::null_message_router void_null_router_t; + + void_router_t* p_void_router; + int_router_t* p_int_router; + + SUITE(test_message_router) + { + //========================================================================= + TEST(message_router_void) + { + RouterVoid1 r1; + RouterVoid2 r2; + + p_void_router = &r1; + + p_void_router->receive(r2, message1); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(0, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(1, r2.callback_count); + + p_void_router->receive(r2, message2); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(2, r2.callback_count); + + p_void_router->receive(r2, message3); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(3, r2.callback_count); + + p_void_router->receive(r2, message4); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(1, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(4, r2.callback_count); + + p_void_router = &r2; + + p_void_router->receive(r1, message1); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(0, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(1, r1.callback_count); + + p_void_router->receive(r1, message2); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(2, r1.callback_count); + + p_void_router->receive(r1, message3); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(3, r1.callback_count); + + p_void_router->receive(r1, message4); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(1, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(4, r1.callback_count); + } + + //========================================================================= + TEST(message_router_int) + { + RouterInt1 r1; + RouterInt2 r2; + + p_int_router = &r1; + + CHECK_EQUAL(int(message1.ID), (p_int_router->receive(r2, message1))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(0, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(1, r2.callback_count); + + CHECK_EQUAL(int(message2.ID), (p_int_router->receive(r2, message2))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(2, r2.callback_count); + + CHECK_EQUAL(int(message3.ID), (p_int_router->receive(r2, message3))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(3, r2.callback_count); + + CHECK_EQUAL(int(message4.ID), (p_int_router->receive(r2, message4))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(1, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(4, r2.callback_count); + + p_int_router = &r2; + + CHECK_EQUAL(int(message1.ID), (p_int_router->receive(r1, message1))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(0, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(1, r1.callback_count); + + CHECK_EQUAL(int(message2.ID), (p_int_router->receive(r1, message2))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(2, r1.callback_count); + + CHECK_EQUAL(int(message3.ID), (p_int_router->receive(r1, message3))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(3, r1.callback_count); + + CHECK_EQUAL(int(message4.ID), (p_int_router->receive(r1, message4))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(1, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(4, r1.callback_count); + } + + //========================================================================= + TEST(message_null_router_void) + { + RouterVoid2 router; + void_null_router_t null_router; + + // Send from the null router. + null_router.send_to(router, message1); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(0, router.message2_count); + CHECK_EQUAL(0, router.message4_count); + CHECK_EQUAL(0, router.message_unknown_count); + + null_router.send_to(router, message2); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(0, router.message4_count); + CHECK_EQUAL(0, router.message_unknown_count); + + null_router.send_to(router, message3); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(0, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + null_router.send_to(router, message4); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + // Send to the null router. + router.send_to(null_router, message1); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + router.send_to(null_router, message2); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + router.send_to(null_router, message3); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + router.send_to(null_router, message4); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + } + }; +} From 2674a81fa2cab97f57ab8b8da5bbaedf57754b3e Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 12 Jun 2017 23:51:27 +0100 Subject: [PATCH 2/5] Experimental container memcpy support --- test/test_string_char.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/test_string_char.cpp b/test/test_string_char.cpp index b525e3ff..29851334 100644 --- a/test/test_string_char.cpp +++ b/test/test_string_char.cpp @@ -3017,5 +3017,27 @@ namespace CHECK_EQUAL(position1, position2); } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + Text& text2(*reinterpret_cast(buffer)); + text2.repair(); + + bool is_equal = Equal(text, text2); + CHECK(is_equal); + + text = "GHIJKL"; + is_equal = Equal(text, text2); + CHECK(!is_equal); + } }; } From 8a6de78bfa399a9b257f396411046a0ac8a4f5a1 Mon Sep 17 00:00:00 2001 From: jwellbelove Date: Fri, 16 Jun 2017 12:43:44 +0100 Subject: [PATCH 3/5] Experimental etl::vector with 'fixup()' --- src/platform.h | 6 +++++ src/private/pvoidvector.h | 13 ++++++++++- src/vector.h | 48 +++++++++++++++++++++++++++++++++------ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/platform.h b/src/platform.h index 2f27b5a9..7887f354 100644 --- a/src/platform.h +++ b/src/platform.h @@ -83,6 +83,12 @@ SOFTWARE. #define ETL_STATIC_ASSERT_SUPPORTED #endif +// Check to see if the compiler supports C++11 type traits. +#if (defined(ETL_COMPILER_MICROSOFT) && (_MSC_VER >= 1600)) || \ + (defined(ETL_COMPILER_GCC) && (__cplusplus >= 201103L)) +#define ETL_C11_TYPE_TRAITS_SUPPORTED +#endif + // Some targets do not support 8bit types. #define ETL_8BIT_SUPPORT (CHAR_BIT == 8) diff --git a/src/private/pvoidvector.h b/src/private/pvoidvector.h index 3d1b0cb1..d34e447f 100644 --- a/src/private/pvoidvector.h +++ b/src/private/pvoidvector.h @@ -464,7 +464,7 @@ namespace etl std::copy(first, last, position); p_end += count; } - + //********************************************************************* /// Erases an element. ///\param i_element Iterator to the element. @@ -566,6 +566,17 @@ namespace etl p_end = p_buffer; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void fixup(void** p_buffer_) + { + uintptr_t length = p_end - p_buffer; + + p_buffer = p_buffer_; + p_end = p_buffer_ + length; + } + void** p_buffer; void** p_end; diff --git a/src/vector.h b/src/vector.h index 5b78b160..24f77d16 100644 --- a/src/vector.h +++ b/src/vector.h @@ -33,6 +33,10 @@ SOFTWARE. #define __ETL_IN_VECTOR_H__ +#ifdef ETL_C11_TYPE_TRAITS_SUPPORTED +#include +#endif + #include #include #include @@ -402,7 +406,7 @@ namespace etl initialise(); -#if defined(ETL_DEBUG) +#if defined(ETL_DEBUG) p_end = etl::uninitialized_copy(first, last, p_buffer, construct_count); #else p_end = etl::uninitialized_copy(first, last, p_buffer); @@ -421,7 +425,7 @@ namespace etl initialise(); -#if defined(ETL_DEBUG) +#if defined(ETL_DEBUG) p_end = etl::uninitialized_fill_n(p_buffer, n, value, construct_count); #else p_end = etl::uninitialized_fill_n(p_buffer, n, value); @@ -751,7 +755,7 @@ namespace etl etl::destroy(p_end - n_delete, p_end, construct_count); #else etl::destroy(p_end - n_delete, p_end); -#endif +#endif p_end -= n_delete; } @@ -833,14 +837,24 @@ namespace etl p_end = p_buffer; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void fixup(T* p_buffer_) + { + uintptr_t length = p_end - p_buffer; + p_buffer = p_buffer_; + p_end = p_buffer_ + length; + } + private: pointer p_buffer; ///< Pointer to the start of the buffer. pointer p_end; ///< Pointer to one past the last element in the buffer. - //********************************************************************* - /// Create a new element with a default value at the back. - //********************************************************************* + //********************************************************************* + /// Create a new element with a default value at the back. + //********************************************************************* inline void create_back() { #if defined(ETL_DEBUG) @@ -1048,11 +1062,23 @@ namespace etl return *this; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void fixup() + { + #ifdef ETL_C11_TYPE_TRAITS_SUPPORTED + ETL_STATIC_ASSERT(std::is_trivially_copyable::value, "The stored type is not compatible with a low level copy"); + #endif + + etl::ivector::fixup(buffer); + } + private: typename etl::aligned_storage::value>::type buffer; }; - + //*************************************************************************** /// A vector implementation that uses a fixed size buffer. ///\tparam T The element type. @@ -1133,6 +1159,14 @@ namespace etl return *this; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void fixup() + { + etl::ivector::fixup(buffer); + } + private: typename etl::aligned_storage::value>::type buffer; From d89e244fac15e06b4bfc562d68d799990e5d8e09 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 17 Jun 2017 08:57:11 +0100 Subject: [PATCH 4/5] Renamed some classes for be compatible with C++11 standards. --- src/memory.h | 68 +++++++++++++++++++++++------------------------ src/type_traits.h | 28 +++++++++---------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/memory.h b/src/memory.h index 1e05579e..947c1124 100644 --- a/src/memory.h +++ b/src/memory.h @@ -55,7 +55,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value) { std::fill(o_begin, o_end, value); @@ -68,7 +68,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value) { typedef typename std::iterator_traits::value_type value_type; @@ -88,7 +88,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value, TCounter& count) { count += std::distance(o_begin, o_end); @@ -104,7 +104,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_fill(TOutputIterator o_begin, TOutputIterator o_end, const T& value, TCounter& count) { count += std::distance(o_begin, o_end); @@ -142,7 +142,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { return std::copy(i_begin, i_end, o_begin); @@ -153,7 +153,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin) { typedef typename std::iterator_traits::value_type value_type; @@ -176,7 +176,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = std::copy(i_begin, i_end, o_begin); @@ -191,7 +191,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_copy(TInputIterator i_begin, TInputIterator i_end, TOutputIterator o_begin, TCounter& count) { TOutputIterator o_end = etl::uninitialized_copy(i_begin, i_end, o_begin); @@ -229,7 +229,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type create_default_at(T* /*p*/) { } @@ -239,7 +239,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type create_default_at(T* /*p*/, TCounter& count) { ++count; @@ -250,7 +250,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type create_default_at(T* p) { ::new (p) T; @@ -261,7 +261,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type create_default_at(T* p, TCounter& count) { ::new (p) T; @@ -273,7 +273,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type uninitialized_default_construct(TOutputIterator /*o_begin*/, TOutputIterator /*o_end*/) { } @@ -283,7 +283,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type uninitialized_default_construct(TOutputIterator o_begin, TOutputIterator o_end) { typedef typename std::iterator_traits::value_type value_type; @@ -301,7 +301,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type uninitialized_default_construct(TOutputIterator o_begin, TOutputIterator o_end, TCounter& count) { count = std::distance(o_begin, o_end); @@ -313,7 +313,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type uninitialized_default_construct(TOutputIterator o_begin, TOutputIterator o_end, TCounter& count) { count += std::distance(o_begin, o_end); @@ -326,7 +326,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_default_construct_n(TOutputIterator o_begin, TSize n) { TOutputIterator o_end = o_begin + n; @@ -339,7 +339,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_default_construct_n(TOutputIterator o_begin, TSize n) { TOutputIterator o_end = o_begin + n; @@ -355,7 +355,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_default_construct_n(TOutputIterator o_begin, TSize n, TCounter& count) { TOutputIterator o_end = o_begin + n; @@ -371,7 +371,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TOutputIterator>::type + typename etl::enable_if::value_type>::value, TOutputIterator>::type uninitialized_default_construct_n(TOutputIterator o_begin, TSize n, TCounter& count) { TOutputIterator o_end = o_begin + n; @@ -430,7 +430,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type uninitialized_value_construct(TOutputIterator o_begin, TOutputIterator o_end) { typedef typename std::iterator_traits::value_type value_type; @@ -443,7 +443,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type uninitialized_value_construct(TOutputIterator o_begin, TOutputIterator o_end) { typedef typename std::iterator_traits::value_type value_type; @@ -504,7 +504,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type destroy_at(T* /*p*/) { } @@ -514,7 +514,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type destroy_at(T* p) { p->~T(); @@ -526,7 +526,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type destroy_at(T* /*p*/, TCounter& count) { --count; @@ -538,7 +538,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type destroy_at(T* p, TCounter& count) { p->~T(); @@ -550,7 +550,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type destroy(TIterator /*i_begin*/, TIterator /*i_end*/) { } @@ -560,7 +560,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type destroy(TIterator i_begin, TIterator i_end) { while (i_begin != i_end) @@ -576,7 +576,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type destroy(TIterator i_begin, TIterator i_end, TCounter& count) { count -= std::distance(i_begin, i_end); @@ -588,7 +588,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, void>::type + typename etl::enable_if::value_type>::value, void>::type destroy(TIterator i_begin, TIterator i_end, TCounter& count) { count -= std::distance(i_begin, i_end); @@ -605,7 +605,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TIterator>::type + typename etl::enable_if::value_type>::value, TIterator>::type destroy_n(TIterator i_begin, TSize n) { return i_begin + n; @@ -616,7 +616,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TIterator>::type + typename etl::enable_if::value_type>::value, TIterator>::type destroy_n(TIterator i_begin, TSize n) { while (n > 0) @@ -635,7 +635,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TIterator>::type + typename etl::enable_if::value_type>::value, TIterator>::type destroy_n(TIterator i_begin, TSize n, TCounter& count) { count -= n; @@ -648,7 +648,7 @@ namespace etl ///\ingroup memory //***************************************************************************** template - typename etl::enable_if::value_type>::value, TIterator>::type + typename etl::enable_if::value_type>::value, TIterator>::type destroy_n(TIterator i_begin, TSize n, TCounter& count) { count -= n; diff --git a/src/type_traits.h b/src/type_traits.h index b2188b87..453423d0 100644 --- a/src/type_traits.h +++ b/src/type_traits.h @@ -47,15 +47,15 @@ namespace etl template struct integral_constant { - static const T value = VALUE; + static const T value = VALUE; - typedef T value_type; + typedef T value_type; typedef integral_constant type; - operator value_type() const - { - return value; - } + operator value_type() const + { + return value; + } }; /// integral_constant specialisations @@ -238,25 +238,25 @@ namespace etl template struct is_pod : etl::integral_constant::value || etl::is_pointer::value> {}; - /// has_trivial_constructor + /// is_trivially_constructible /// For C++03, only POD types are recognised. ///\ingroup type_traits - template struct has_trivial_constructor : etl::is_pod {}; + template struct is_trivially_constructible : etl::is_pod {}; - /// has_trivial_copy_constructor + /// is_trivially_copy_constructible /// For C++03, only POD types are recognised. ///\ingroup type_traits - template struct has_trivial_copy_constructor : etl::is_pod {}; + template struct is_trivially_copy_constructible : etl::is_pod {}; - /// has_trivial_destructor + /// is_trivially_destructible /// For C++03, only POD types are recognised. ///\ingroup type_traits - template struct has_trivial_destructor : etl::is_pod {}; + template struct is_trivially_destructible : etl::is_pod {}; - /// has_trivial_assignment + /// is_trivially_copy_assignable /// For C++03, only POD types are recognised. ///\ingroup type_traits - template struct has_trivial_assignment : etl::is_pod {}; + template struct is_trivially_copy_assignable : etl::is_pod {}; /// conditional ///\ingroup type_traits From 97ead6fc277886d7d816b2d3841198c9612d87b9 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 17 Jun 2017 15:33:04 +0100 Subject: [PATCH 5/5] Added repair functions to allow certain containers to copied with low level functions such as memcpy. --- src/basic_string.h | 15 ++ src/cstring.h | 12 +- src/deque.h | 46 ++++- src/platform.h | 5 + src/private/pvoidvector.h | 2 +- src/private/vector_base.h | 14 ++ src/u16string.h | 8 + src/u32string.h | 8 + src/vector.h | 39 +++-- src/wstring.h | 8 + test/codeblocks/ETL.depend | 142 ++++++++------- test/codeblocks/ETL.layout | 295 ++++++++++++++++++-------------- test/test_deque.cpp | 82 +++++++++ test/test_string_char.cpp | 40 ++++- test/test_string_u16.cpp | 50 ++++++ test/test_string_u32.cpp | 50 ++++++ test/test_string_wchar_t.cpp | 50 ++++++ test/test_vector.cpp | 77 ++++++++- test/test_vector_pointer.cpp | 44 ++++- test/vs2017/etl.vcxproj | 4 +- test/vs2017/etl.vcxproj.filters | 17 +- 21 files changed, 759 insertions(+), 249 deletions(-) diff --git a/src/basic_string.h b/src/basic_string.h index 0cb56f2c..36045b0a 100644 --- a/src/basic_string.h +++ b/src/basic_string.h @@ -1890,6 +1890,13 @@ namespace etl return *this; } +#ifdef ETL_ISTRING_REPAIR_ENABLE + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + virtual void repair() = 0; +#endif + protected: //********************************************************************* @@ -1910,6 +1917,14 @@ namespace etl p_buffer[0] = 0; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void repair(T* p_buffer_) + { + p_buffer = p_buffer_; + } + private: //************************************************************************* diff --git a/src/cstring.h b/src/cstring.h index 66d97f42..9686c211 100644 --- a/src/cstring.h +++ b/src/cstring.h @@ -73,7 +73,6 @@ namespace etl string(const etl::string& other) : istring(reinterpret_cast(&buffer), MAX_SIZE) { - istring::initialise(); istring::assign(other.begin(), other.end()); } @@ -91,7 +90,6 @@ namespace etl // Set the length to the exact amount. length = (length > MAX_SIZE_) ? MAX_SIZE_ : length; - istring::initialise(); istring::assign(other.begin() + position, other.begin() + position + length); } @@ -102,7 +100,6 @@ namespace etl string(const value_type* text) : istring(reinterpret_cast(&buffer), MAX_SIZE) { - istring::initialise(); istring::assign(text, text + etl::char_traits::length(text)); } @@ -114,7 +111,6 @@ namespace etl string(const value_type* text, size_t count) : istring(reinterpret_cast(&buffer), MAX_SIZE) { - istring::initialise(); istring::assign(text, text + count); } @@ -177,6 +173,14 @@ namespace etl return *this; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void repair() + { + etl::istring::repair(buffer); + } + private: value_type buffer[MAX_SIZE + 1]; diff --git a/src/deque.h b/src/deque.h index 7ac9d71e..03f8c71e 100644 --- a/src/deque.h +++ b/src/deque.h @@ -113,6 +113,20 @@ namespace etl } }; + //*************************************************************************** + ///\ingroup vector + /// Deque incompatible type exception. + //*************************************************************************** + class deque_incompatible_type : public deque_exception + { + public: + + deque_incompatible_type(string_type file_name, numeric_type line_number) + : deque_exception(ETL_ERROR_TEXT("deque:type", ETL_FILE"D"), file_name, line_number) + { + } + }; + //*************************************************************************** /// The base class for all templated deque types. ///\ingroup deque @@ -1358,6 +1372,13 @@ namespace etl return *this; } +#ifdef ETL_IDEQUE_REPAIR_ENABLE + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + virtual void repair() = 0; +#endif + protected: //************************************************************************* @@ -1380,7 +1401,18 @@ namespace etl } _begin = iterator(0, *this, p_buffer); - _end = iterator(0, *this, p_buffer); + _end = iterator(0, *this, p_buffer); + } + + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void repair(pointer p_buffer_) + { + p_buffer = p_buffer_; + + _begin = iterator(_begin.index, *this, p_buffer); + _end = iterator(_end.index, *this, p_buffer); } iterator _begin; ///Iterator to the _begin item in the deque. @@ -1613,6 +1645,18 @@ namespace etl return *this; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void repair() + { +#ifdef ETL_C11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + ETL_ASSERT(std::is_trivially_copyable::value, ETL_ERROR(etl::deque_incompatible_type)); +#endif + + etl::ideque::repair(reinterpret_cast(&buffer[0])); + } + private: /// The uninitialised buffer of T used in the deque. diff --git a/src/platform.h b/src/platform.h index 7887f354..a9c3efee 100644 --- a/src/platform.h +++ b/src/platform.h @@ -89,6 +89,11 @@ SOFTWARE. #define ETL_C11_TYPE_TRAITS_SUPPORTED #endif +#if (defined(ETL_COMPILER_MICROSOFT) && (_MSC_VER >= 1600)) || \ + (defined(ETL_COMPILER_GCC) && (__cplusplus >= 201402L)) +#define ETL_C11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED +#endif + // Some targets do not support 8bit types. #define ETL_8BIT_SUPPORT (CHAR_BIT == 8) diff --git a/src/private/pvoidvector.h b/src/private/pvoidvector.h index d34e447f..f81c228d 100644 --- a/src/private/pvoidvector.h +++ b/src/private/pvoidvector.h @@ -569,7 +569,7 @@ namespace etl //************************************************************************* /// Fix the internal pointers after a low level memory copy. //************************************************************************* - void fixup(void** p_buffer_) + void repair(void** p_buffer_) { uintptr_t length = p_end - p_buffer; diff --git a/src/private/vector_base.h b/src/private/vector_base.h index ed408926..b39f8dfe 100644 --- a/src/private/vector_base.h +++ b/src/private/vector_base.h @@ -101,6 +101,20 @@ namespace etl } }; + //*************************************************************************** + ///\ingroup vector + /// Vector incompatible type exception. + //*************************************************************************** + class vector_incompatible_type : public vector_exception + { + public: + + vector_incompatible_type(string_type file_name, numeric_type line_number) + : vector_exception(ETL_ERROR_TEXT("vector:type", ETL_FILE"D"), file_name, line_number) + { + } + }; + //*************************************************************************** ///\ingroup vector /// The base class for all templated vector types. diff --git a/src/u16string.h b/src/u16string.h index 55a39084..2e760996 100644 --- a/src/u16string.h +++ b/src/u16string.h @@ -177,6 +177,14 @@ namespace etl return *this; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void repair() + { + etl::iu16string::repair(buffer); + } + private: value_type buffer[MAX_SIZE + 1]; diff --git a/src/u32string.h b/src/u32string.h index 6d16f36c..877c7a0d 100644 --- a/src/u32string.h +++ b/src/u32string.h @@ -177,6 +177,14 @@ namespace etl return *this; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void repair() + { + etl::iu32string::repair(buffer); + } + private: value_type buffer[MAX_SIZE + 1]; diff --git a/src/vector.h b/src/vector.h index 24f77d16..38e438ef 100644 --- a/src/vector.h +++ b/src/vector.h @@ -224,7 +224,7 @@ namespace etl ///\param value The value to fill new elements with. Default = default constructed value. //********************************************************************* template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type resize(size_t new_size, T value) { ETL_ASSERT(new_size <= MAX_SIZE, ETL_ERROR(vector_full)); @@ -258,7 +258,7 @@ namespace etl ///\param value The value to fill new elements with. Default = default constructed value. //********************************************************************* template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type resize(size_t new_size, T value) { ETL_ASSERT(new_size <= MAX_SIZE, ETL_ERROR(vector_full)); @@ -399,6 +399,8 @@ namespace etl template void assign(TIterator first, TIterator last) { + STATIC_ASSERT((etl::is_same::type, typename etl::remove_cv::value_type>::type>::value), "Iterator type does not match container type"); + #if defined(ETL_DEBUG) difference_type count = std::distance(first, last); ETL_ASSERT(static_cast(count) <= MAX_SIZE, ETL_ERROR(vector_full)); @@ -510,7 +512,7 @@ namespace etl ///\param value The value to insert. //********************************************************************* template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type insert(iterator position, size_t n, parameter_t value) { ETL_ASSERT((size() + n) <= MAX_SIZE, ETL_ERROR(vector_full)); @@ -530,7 +532,7 @@ namespace etl ///\param value The value to insert. //********************************************************************* template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type insert(iterator position, size_t n, parameter_t value) { ETL_ASSERT((size() + n) <= MAX_SIZE, ETL_ERROR(vector_full)); @@ -598,7 +600,7 @@ namespace etl ///\param last The last + 1 element to add. //********************************************************************* template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type insert(iterator position, TIterator first, TIterator last) { size_t count = std::distance(first, last); @@ -628,7 +630,7 @@ namespace etl ///\param last The last + 1 element to add. //********************************************************************* template - typename etl::enable_if::value, void>::type + typename etl::enable_if::value, void>::type insert(iterator position, TIterator first, TIterator last) { size_t count = std::distance(first, last); @@ -711,7 +713,7 @@ namespace etl ///\return An iterator pointing to the element that followed the erased element. //********************************************************************* template - typename etl::enable_if::value, iterator>::type + typename etl::enable_if::value, iterator>::type erase(iterator first, iterator last) { if (first == begin() && last == end()) @@ -738,7 +740,7 @@ namespace etl ///\return An iterator pointing to the element that followed the erased element. //********************************************************************* template - typename etl::enable_if::value, iterator>::type + typename etl::enable_if::value, iterator>::type erase(iterator first, iterator last) { if (first == begin() && last == end()) @@ -811,6 +813,13 @@ namespace etl return max_size() - size(); } +#ifdef ETL_IVECTOR_REPAIR_ENABLE + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + virtual void repair() = 0; +#endif + protected: //********************************************************************* @@ -840,7 +849,7 @@ namespace etl //************************************************************************* /// Fix the internal pointers after a low level memory copy. //************************************************************************* - void fixup(T* p_buffer_) + void repair(T* p_buffer_) { uintptr_t length = p_end - p_buffer; p_buffer = p_buffer_; @@ -1065,13 +1074,13 @@ namespace etl //************************************************************************* /// Fix the internal pointers after a low level memory copy. //************************************************************************* - void fixup() + void repair() { - #ifdef ETL_C11_TYPE_TRAITS_SUPPORTED - ETL_STATIC_ASSERT(std::is_trivially_copyable::value, "The stored type is not compatible with a low level copy"); + #ifdef ETL_C11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED + ETL_ASSERT(std::is_trivially_copyable::value, ETL_ERROR(etl::vector_incompatible_type)); #endif - etl::ivector::fixup(buffer); + etl::ivector::repair(buffer); } private: @@ -1162,9 +1171,9 @@ namespace etl //************************************************************************* /// Fix the internal pointers after a low level memory copy. //************************************************************************* - void fixup() + void repair() { - etl::ivector::fixup(buffer); + etl::ivector::repair(buffer); } private: diff --git a/src/wstring.h b/src/wstring.h index 0f015e00..4434f1c2 100644 --- a/src/wstring.h +++ b/src/wstring.h @@ -178,6 +178,14 @@ namespace etl return *this; } + //************************************************************************* + /// Fix the internal pointers after a low level memory copy. + //************************************************************************* + void repair() + { + etl::iwstring::repair(buffer); + } + private: value_type buffer[MAX_SIZE + 1]; diff --git a/test/codeblocks/ETL.depend b/test/codeblocks/ETL.depend index f3bcb39a..1b401b1f 100644 --- a/test/codeblocks/ETL.depend +++ b/test/codeblocks/ETL.depend @@ -3289,7 +3289,7 @@ "CurrentTest.h" "ReportAssertImpl.h" -1494283539 source:d:\users\john\documents\programming\github\etl\test\test_algorithm.cpp +1497706369 source:d:\users\john\documents\programming\github\etl\test\test_algorithm.cpp "UnitTest++.h" "algorithm.h" "container.h" @@ -3315,7 +3315,7 @@ -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_alignment.cpp +1496992773 source:d:\users\john\documents\programming\github\etl\test\test_alignment.cpp "UnitTest++.h" "alignment.h" "type_traits.h" @@ -3423,7 +3423,7 @@ "exception.h" "error_handler.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_bitset.cpp +1497690249 source:d:\users\john\documents\programming\github\etl\test\test_bitset.cpp "UnitTest++.h" @@ -3538,7 +3538,7 @@ "static_assert.h" "exception.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_deque.cpp +1497708284 source:d:\users\john\documents\programming\github\etl\test\test_deque.cpp "UnitTest++.h" "ExtraCheckMacros.h" "deque.h" @@ -3548,6 +3548,7 @@ + 1494277861 d:\users\john\documents\programming\github\etl\test\extracheckmacros.h "../unittest-cpp/UnitTest++/HelperMacros.h" @@ -3582,7 +3583,7 @@ "exception.h" -1488104217 d:\users\john\documents\programming\github\etl\test\data.h +1497685058 d:\users\john\documents\programming\github\etl\test\data.h 1494277861 source:d:\users\john\documents\programming\github\etl\test\test_endian.cpp @@ -3612,7 +3613,7 @@ "exception.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_flat_map.cpp +1497306512 source:d:\users\john\documents\programming\github\etl\test\test_flat_map.cpp "UnitTest++.h" @@ -3726,7 +3727,7 @@ 1482623723 v_1.h" -1494283539 source:d:\users\john\documents\programming\github\etl\test\test_forward_list.cpp +1497685058 source:d:\users\john\documents\programming\github\etl\test\test_forward_list.cpp "UnitTest++.h" "ExtraCheckMacros.h" "data.h" @@ -3783,7 +3784,7 @@ 1479511692 nction.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_functional.cpp +1497685058 source:d:\users\john\documents\programming\github\etl\test\test_functional.cpp "UnitTest++.h" "functional.h" @@ -3843,7 +3844,7 @@ 1450265856 d:\users\john\documents\programming\github\etl\largest.h "type_traits.h" -1494283539 source:d:\users\john\documents\programming\github\etl\test\test_list.cpp +1497685059 source:d:\users\john\documents\programming\github\etl\test\test_list.cpp "UnitTest++.h" "ExtraCheckMacros.h" "list.h" @@ -3968,11 +3969,12 @@ "exception.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_vector.cpp +1497712887 source:d:\users\john\documents\programming\github\etl\test\test_vector.cpp "UnitTest++.h" + "vector.h" 1479511692 ctor.h" @@ -5230,7 +5232,7 @@ 1452516033 /home/jwellbelove/Programming/etl/visitor.h -1494283539 source:d:\users\john\documents\programming\github\etl\test\test_intrusive_forward_list.cpp +1497685059 source:d:\users\john\documents\programming\github\etl\test\test_intrusive_forward_list.cpp "UnitTest++.h" "ExtraCheckMacros.h" "data.h" @@ -5309,7 +5311,7 @@ "iterator.h" "type_traits.h" -1494277861 d:\users\john\documents\programming\github\etl\src\type_traits.h +1497689716 d:\users\john\documents\programming\github\etl\src\type_traits.h "platform.h" "nullptr.h" @@ -5318,7 +5320,7 @@ -1492164295 d:\users\john\documents\programming\github\etl\src\alignment.h +1496993010 d:\users\john\documents\programming\github\etl\src\alignment.h "type_traits.h" "static_assert.h" @@ -5326,7 +5328,7 @@ 1482948766 d:\users\john\documents\programming\github\etl\src\static_assert.h "platform.h" -1494277860 d:\users\john\documents\programming\github\etl\src\array.h +1497306512 d:\users\john\documents\programming\github\etl\src\array.h @@ -5370,7 +5372,7 @@ "integral_limits.h" -1494277860 d:\users\john\documents\programming\github\etl\src\bitset.h +1497685058 d:\users\john\documents\programming\github\etl\src\bitset.h @@ -5480,7 +5482,7 @@ "static_assert.h" "exception.h" -1494277860 d:\users\john\documents\programming\github\etl\src\deque.h +1497708272 d:\users\john\documents\programming\github\etl\src\deque.h @@ -5512,22 +5514,9 @@ 1482948766 d:\users\john\documents\programming\github\etl\src\fixed_iterator.h -1494277860 d:\users\john\documents\programming\github\etl\src\flat_map.h - - - - - - - "platform.h" - "vector.h" +1497712590 d:\users\john\documents\programming\github\etl\src\flat_map.h + "reference_flat_map.h" "pool.h" - "exception.h" - "vector.h" - "error_handler.h" - "debug_count.h" - "type_traits.h" - "parameter_type.h" 1482748252 d:\users\john\documents\programming\github\etl\src\iflat_map.h @@ -5562,13 +5551,14 @@ 1482748239 vectorpointer.h" -1494277860 d:\users\john\documents\programming\github\etl\src\private\vector_base.h +1497705958 d:\users\john\documents\programming\github\etl\src\private\vector_base.h "../exception.h" "../error_handler.h" "../debug_count.h" -1494277861 d:\users\john\documents\programming\github\etl\src\vector.h +1497712492 d:\users\john\documents\programming\github\etl\src\vector.h + @@ -5624,7 +5614,7 @@ "../ivector.h" "../error_handler.h" -1494277860 d:\users\john\documents\programming\github\etl\src\forward_list.h +1497685058 d:\users\john\documents\programming\github\etl\src\forward_list.h @@ -5680,7 +5670,7 @@ "../exception.h" "../error_handler.h" -1479515290 d:\users\john\documents\programming\github\etl\src\functional.h +1494763312 d:\users\john\documents\programming\github\etl\src\functional.h 1482948766 d:\users\john\documents\programming\github\etl\src\hash.h @@ -5691,7 +5681,7 @@ 1479515290 d:\users\john\documents\programming\github\etl\src\instance_count.h -1488104217 d:\users\john\documents\programming\github\etl\src\intrusive_forward_list.h +1497685058 d:\users\john\documents\programming\github\etl\src\intrusive_forward_list.h "platform.h" @@ -5715,7 +5705,7 @@ "exception.h" "error_handler.h" -1494277860 d:\users\john\documents\programming\github\etl\src\io_port.h +1497308571 d:\users\john\documents\programming\github\etl\src\io_port.h "nullptr.h" @@ -5724,7 +5714,7 @@ 1485905321 d:\users\john\documents\programming\github\etl\src\largest.h "type_traits.h" -1494277860 d:\users\john\documents\programming\github\etl\src\list.h +1497685058 d:\users\john\documents\programming\github\etl\src\list.h @@ -5757,7 +5747,7 @@ "../exception.h" "../error_handler.h" -1494277860 d:\users\john\documents\programming\github\etl\src\map.h +1497306512 d:\users\john\documents\programming\github\etl\src\map.h @@ -5835,7 +5825,7 @@ "../exception.h" "../error_handler.h" -1494277861 d:\users\john\documents\programming\github\etl\src\set.h +1497685058 d:\users\john\documents\programming\github\etl\src\set.h @@ -5908,7 +5898,7 @@ 1482624915 ordered_map.h" -1494277861 d:\users\john\documents\programming\github\etl\src\unordered_map.h +1497306512 d:\users\john\documents\programming\github\etl\src\unordered_map.h @@ -5961,7 +5951,7 @@ 1479515291 d:\users\john\documents\programming\github\etl\src\visitor.h -1494277860 d:\users\john\documents\programming\github\etl\src\platform.h +1497712972 d:\users\john\documents\programming\github\etl\src\platform.h @@ -5997,7 +5987,7 @@ 1479511691 at_multimap.h" -1494277860 d:\users\john\documents\programming\github\etl\src\flat_multimap.h +1497306512 d:\users\john\documents\programming\github\etl\src\flat_multimap.h @@ -6087,7 +6077,7 @@ 1482358550 trusive_links.h" -1494283539 source:d:\users\john\documents\programming\github\etl\test\test_intrusive_list.cpp +1497685059 source:d:\users\john\documents\programming\github\etl\test\test_intrusive_list.cpp "UnitTest++.h" "ExtraCheckMacros.h" "data.h" @@ -6098,7 +6088,7 @@ -1488104217 d:\users\john\documents\programming\github\etl\src\intrusive_list.h +1497685058 d:\users\john\documents\programming\github\etl\src\intrusive_list.h "platform.h" @@ -6144,7 +6134,7 @@ 1479511692 ltimap.h" -1494277860 d:\users\john\documents\programming\github\etl\src\multimap.h +1497306512 d:\users\john\documents\programming\github\etl\src\multimap.h @@ -6189,7 +6179,7 @@ 1479511692 ltiset.h" -1494277860 d:\users\john\documents\programming\github\etl\src\multiset.h +1497306512 d:\users\john\documents\programming\github\etl\src\multiset.h @@ -6301,7 +6291,7 @@ 1482625301 ordered_multimap.h" -1494277861 d:\users\john\documents\programming\github\etl\src\unordered_multimap.h +1497306512 d:\users\john\documents\programming\github\etl\src\unordered_multimap.h @@ -6354,7 +6344,7 @@ "unordered_multiset.h" "checksum.h" -1494277861 d:\users\john\documents\programming\github\etl\src\unordered_multiset.h +1497306512 d:\users\john\documents\programming\github\etl\src\unordered_multiset.h @@ -6404,7 +6394,7 @@ "unordered_set.h" "checksum.h" -1494277861 d:\users\john\documents\programming\github\etl\src\unordered_set.h +1497306512 d:\users\john\documents\programming\github\etl\src\unordered_set.h @@ -6480,7 +6470,7 @@ 1482079098 or.h" -1494277861 d:\users\john\documents\programming\github\etl\src\private\pvoidvector.h +1497685165 d:\users\john\documents\programming\github\etl\src\private\pvoidvector.h @@ -6530,7 +6520,7 @@ "error_handler.h" "intrusive_links.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_string_char.cpp +1497705758 source:d:\users\john\documents\programming\github\etl\test\test_string_char.cpp "UnitTest++.h" @@ -6539,12 +6529,12 @@ 1482181573 tring.h" -1494277860 d:\users\john\documents\programming\github\etl\src\cstring.h +1497690092 d:\users\john\documents\programming\github\etl\src\cstring.h "platform.h" "basic_string.h" "hash.h" -1494277860 d:\users\john\documents\programming\github\etl\src\basic_string.h +1497702020 d:\users\john\documents\programming\github\etl\src\basic_string.h @@ -6583,7 +6573,7 @@ "../exception.h" "../error_handler.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_string_u16.cpp +1497705758 source:d:\users\john\documents\programming\github\etl\test\test_string_u16.cpp "UnitTest++.h" @@ -6592,12 +6582,12 @@ 1482099557 6string.h" -1482948766 d:\users\john\documents\programming\github\etl\src\u16string.h +1497690510 d:\users\john\documents\programming\github\etl\src\u16string.h "platform.h" "basic_string.h" "hash.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_string_u32.cpp +1497705758 source:d:\users\john\documents\programming\github\etl\test\test_string_u32.cpp "UnitTest++.h" @@ -6606,19 +6596,19 @@ 1482099666 2string.h" -1482948766 d:\users\john\documents\programming\github\etl\src\u32string.h +1497690510 d:\users\john\documents\programming\github\etl\src\u32string.h "platform.h" "basic_string.h" "hash.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_string_wchar_t.cpp +1497705758 source:d:\users\john\documents\programming\github\etl\test\test_string_wchar_t.cpp "UnitTest++.h" "wstring.h" -1482948766 d:\users\john\documents\programming\github\etl\src\wstring.h +1497690510 d:\users\john\documents\programming\github\etl\src\wstring.h "platform.h" "basic_string.h" "hash.h" @@ -6641,11 +6631,12 @@ 1482948766 d:\users\john\documents\programming\github\etl\src\utility.h "type_traits.h" -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_vector_pointer.cpp +1497712935 source:d:\users\john\documents\programming\github\etl\test\test_vector_pointer.cpp "UnitTest++.h" + "vector.h" 1482076051 source:/mnt/hgfs/Programming/GitHub/unittest-cpp/UnitTest++/RequiredCheckException.cpp @@ -7862,7 +7853,7 @@ "type_traits.h" -1488104217 d:\users\john\documents\programming\github\etl\src\memory.h +1497689774 d:\users\john\documents\programming\github\etl\src\memory.h "type_traits.h" @@ -7981,7 +7972,7 @@ -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_random.cpp +1497684591 source:d:\users\john\documents\programming\github\etl\test\test_random.cpp "UnitTest++.h" "random.h" @@ -7989,7 +7980,7 @@ -1494277861 source:d:\users\john\documents\programming\github\etl\test\test_vector_non_trivial.cpp +1497685854 source:d:\users\john\documents\programming\github\etl\test\test_vector_non_trivial.cpp "UnitTest++.h" @@ -8173,7 +8164,7 @@ "HelperMacros.h" -1494362778 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_map.cpp +1497308006 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_map.cpp "UnitTest++.h" @@ -8186,13 +8177,18 @@ "data.h" "reference_flat_map.h" -1494362778 d:\users\john\documents\programming\github\etl\src\reference_flat_map.h +1497308006 d:\users\john\documents\programming\github\etl\src\reference_flat_map.h - "exception.h" + "platform.h" "vector.h" "error_handler.h" + "debug_count.h" + "type_traits.h" + "parameter_type.h" + "exception.h" + "static_assert.h" -1494362778 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_multimap.cpp +1494363271 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_multimap.cpp "UnitTest++.h" @@ -8204,14 +8200,14 @@ "data.h" "reference_flat_multimap.h" -1494362778 d:\users\john\documents\programming\github\etl\src\reference_flat_multimap.h +1497685058 d:\users\john\documents\programming\github\etl\src\reference_flat_multimap.h "exception.h" "error_handler.h" "debug_count.h" "vector.h" -1494362778 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_multiset.cpp +1494363271 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_multiset.cpp "UnitTest++.h" @@ -8223,7 +8219,7 @@ "data.h" "reference_flat_multiset.h" -1494362778 d:\users\john\documents\programming\github\etl\src\reference_flat_multiset.h +1494363271 d:\users\john\documents\programming\github\etl\src\reference_flat_multiset.h @@ -8236,7 +8232,7 @@ "error_handler.h" "exception.h" -1494362778 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_set.cpp +1494363271 source:d:\users\john\documents\programming\github\etl\test\test_reference_flat_set.cpp "UnitTest++.h" @@ -8248,7 +8244,7 @@ "data.h" "reference_flat_set.h" -1494362778 d:\users\john\documents\programming\github\etl\src\reference_flat_set.h +1494363271 d:\users\john\documents\programming\github\etl\src\reference_flat_set.h diff --git a/test/codeblocks/ETL.layout b/test/codeblocks/ETL.layout index c0d3ac7a..542b69b9 100644 --- a/test/codeblocks/ETL.layout +++ b/test/codeblocks/ETL.layout @@ -2,19 +2,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + + + + + + + + + + + @@ -22,9 +97,89 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -37,64 +192,14 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -102,74 +207,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/test_deque.cpp b/test/test_deque.cpp index 12465f81..7ef3373f 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -38,6 +38,7 @@ SOFTWARE. #include #include #include +#include namespace { @@ -48,6 +49,8 @@ namespace typedef TestDataDC DC; typedef TestDataNDC NDC; + typedef etl::deque DataInt; + typedef etl::ideque IDataInt; typedef etl::deque DataDC; typedef etl::deque DataNDC; typedef etl::ideque IDataNDC; @@ -82,6 +85,9 @@ namespace std::vector initial_data_small = { N0, N1, N2, N3, N4, N5, N6, N7, N8, N9 }; std::vector insert_data = { N10, N11, N12, N13, N14 }; std::vector initial_data_dc = { DC("0"), DC("1"), DC("2"), DC("3"), DC("4"), DC("5"), DC("6"), DC("7"), DC("8"), DC("9"), DC("10"), DC("11"), DC("12"), DC("13") }; + std::vector int_data1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; + std::vector int_data2 = { 15, 16, 17, 18 }; + //************************************************************************* TEST(test_constructor) @@ -1514,5 +1520,81 @@ namespace CHECK(data.rbegin() == data.rend()); CHECK(data.crbegin() == data.crend()); } + + //************************************************************************* + TEST(test_memcpy_repair) + { + DataInt data(int_data1.begin(), int_data1.end()); + data.pop_front(); + data.pop_front(); + data.pop_front(); + data.pop_front(); + data.insert(data.end(), int_data2.begin(), int_data2.end()); + + char buffer[sizeof(DataInt)]; + + memcpy(&buffer, &data, sizeof(data)); + + DataInt& rdata(*reinterpret_cast(buffer)); + rdata.repair(); + + // Check that the memcpy'd vector is the same. + CHECK_EQUAL(data.size(), rdata.size()); + CHECK(!rdata.empty()); + CHECK(rdata.full()); + + bool is_equal = std::equal(rdata.begin(), + rdata.end(), + data.begin()); + + CHECK(is_equal); + + // Modify the original and check that the memcpy'd vector is not the same. + std::reverse(data.begin(), data.end()); + + is_equal = std::equal(rdata.begin(), + rdata.end(), + data.begin()); + + CHECK(!is_equal); + } + + //************************************************************************* + TEST(test_memcpy_repair_virtual) + { + DataInt data(int_data1.begin(), int_data1.end()); + data.pop_front(); + data.pop_front(); + data.pop_front(); + data.pop_front(); + data.insert(data.end(), int_data2.begin(), int_data2.end()); + + char buffer[sizeof(DataInt)]; + + memcpy(&buffer, &data, sizeof(data)); + + IDataInt& idata(*reinterpret_cast(buffer)); + idata.repair(); + + // Check that the memcpy'd vector is the same. + CHECK_EQUAL(data.size(), idata.size()); + CHECK(!idata.empty()); + CHECK(idata.full()); + + bool is_equal = std::equal(idata.begin(), + idata.end(), + data.begin()); + + CHECK(is_equal); + + // Modify the original and check that the memcpy'd vector is not the same. + std::reverse(data.begin(), data.end()); + + is_equal = std::equal(idata.begin(), + idata.end(), + data.begin()); + + CHECK(!is_equal); + } }; } diff --git a/test/test_string_char.cpp b/test/test_string_char.cpp index 29851334..5b311949 100644 --- a/test/test_string_char.cpp +++ b/test/test_string_char.cpp @@ -3019,7 +3019,7 @@ namespace } //************************************************************************* - TEST_FIXTURE(SetupFixture, test_memcpy) + TEST_FIXTURE(SetupFixture, test_memcpy_repair) { Text text; @@ -3029,14 +3029,42 @@ namespace memcpy(&buffer, &text, sizeof(text)); - Text& text2(*reinterpret_cast(buffer)); - text2.repair(); + Text& rtext(*reinterpret_cast(buffer)); + rtext.repair(); - bool is_equal = Equal(text, text2); + CHECK(!rtext.empty()); + CHECK(!rtext.full()); + + bool is_equal = Equal(text, rtext); CHECK(is_equal); - text = "GHIJKL"; - is_equal = Equal(text, text2); + text = STR("GHIJKL"); + is_equal = Equal(text, rtext); + CHECK(!is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair_virtual) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + IText& itext(*reinterpret_cast(buffer)); + itext.repair(); + + CHECK(!itext.empty()); + CHECK(!itext.full()); + + bool is_equal = Equal(text, itext); + CHECK(is_equal); + + text = STR("GHIJKL"); + is_equal = Equal(text, itext); CHECK(!is_equal); } }; diff --git a/test/test_string_u16.cpp b/test/test_string_u16.cpp index cd1fe7ff..73afca91 100644 --- a/test/test_string_u16.cpp +++ b/test/test_string_u16.cpp @@ -3017,5 +3017,55 @@ namespace CHECK_EQUAL(position1, position2); } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + Text& rtext(*reinterpret_cast(buffer)); + rtext.repair(); + + CHECK(!rtext.empty()); + CHECK(!rtext.full()); + + bool is_equal = Equal(text, rtext); + CHECK(is_equal); + + text = STR("GHIJKL"); + is_equal = Equal(text, rtext); + CHECK(!is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair_virtual) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + IText& itext(*reinterpret_cast(buffer)); + itext.repair(); + + CHECK(!itext.empty()); + CHECK(!itext.full()); + + bool is_equal = Equal(text, itext); + CHECK(is_equal); + + text = STR("GHIJKL"); + is_equal = Equal(text, itext); + CHECK(!is_equal); + } }; } diff --git a/test/test_string_u32.cpp b/test/test_string_u32.cpp index 165a0743..a49aae22 100644 --- a/test/test_string_u32.cpp +++ b/test/test_string_u32.cpp @@ -3017,5 +3017,55 @@ namespace CHECK_EQUAL(position1, position2); } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + Text& rtext(*reinterpret_cast(buffer)); + rtext.repair(); + + CHECK(!rtext.empty()); + CHECK(!rtext.full()); + + bool is_equal = Equal(text, rtext); + CHECK(is_equal); + + text = STR("GHIJKL"); + is_equal = Equal(text, rtext); + CHECK(!is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair_virtual) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + IText& itext(*reinterpret_cast(buffer)); + itext.repair(); + + CHECK(!itext.empty()); + CHECK(!itext.full()); + + bool is_equal = Equal(text, itext); + CHECK(is_equal); + + text = STR("GHIJKL"); + is_equal = Equal(text, itext); + CHECK(!is_equal); + } }; } diff --git a/test/test_string_wchar_t.cpp b/test/test_string_wchar_t.cpp index 61604113..f19decae 100644 --- a/test/test_string_wchar_t.cpp +++ b/test/test_string_wchar_t.cpp @@ -3017,6 +3017,56 @@ namespace CHECK_EQUAL(position1, position2); } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + Text& rtext(*reinterpret_cast(buffer)); + rtext.repair(); + + CHECK(!rtext.empty()); + CHECK(!rtext.full()); + + bool is_equal = Equal(text, rtext); + CHECK(is_equal); + + text = STR("GHIJKL"); + is_equal = Equal(text, rtext); + CHECK(!is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair_virtual) + { + Text text; + + text.assign(STR("ABCDEF")); + + char buffer[sizeof(Text)]; + + memcpy(&buffer, &text, sizeof(text)); + + IText& itext(*reinterpret_cast(buffer)); + itext.repair(); + + CHECK(!itext.empty()); + CHECK(!itext.full()); + + bool is_equal = Equal(text, itext); + CHECK(is_equal); + + text = STR("GHIJKL"); + is_equal = Equal(text, itext); + CHECK(!is_equal); + } }; } diff --git a/test/test_vector.cpp b/test/test_vector.cpp index 4a692376..11c1f954 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -31,11 +31,12 @@ SOFTWARE. #include #include #include +#include #include "vector.h" namespace -{ +{ SUITE(test_vector) { static const size_t SIZE = 10; @@ -146,7 +147,7 @@ namespace Data data(initial_data.begin(), initial_data.end()); Data data2(data); CHECK(data2 == data); - + data2[2] = -1; CHECK(data2 != data); } @@ -624,7 +625,7 @@ namespace const size_t INITIAL_SIZE = 5; const size_t INSERT_SIZE = 3; const int INITIAL_VALUE = 11; - + for (size_t offset = 0; offset <= INITIAL_SIZE; ++offset) { Compare_Data compare_data; @@ -718,7 +719,7 @@ namespace offset = 4; CHECK_THROW(data.insert(data.begin() + offset, initial_data.begin(), initial_data.end()), etl::vector_full); - + offset = data.size(); CHECK_THROW(data.insert(data.begin() + offset, initial_data.begin(), initial_data.end()), etl::vector_full); @@ -762,7 +763,7 @@ namespace CHECK(is_equal); } - + //************************************************************************* TEST_FIXTURE(SetupFixture, test_clear) { @@ -955,5 +956,71 @@ namespace const Data initial2(initial_data.begin(), initial_data.end()); CHECK((initial >= initial2) == (initial_data >= initial_data)); } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair) + { + Data data(initial_data.begin(), initial_data.end()); + + char buffer[sizeof(Data)]; + + memcpy(&buffer, &data, sizeof(data)); + + Data& rdata(*reinterpret_cast(buffer)); + rdata.repair(); + + // Check that the memcpy'd vector is the same. + CHECK_EQUAL(data.size(), rdata.size()); + CHECK(!rdata.empty()); + CHECK(rdata.full()); + + bool is_equal = std::equal(rdata.begin(), + rdata.end(), + data.begin()); + + CHECK(is_equal); + + // Modify the original and check that the memcpy'd vector is not the same. + std::reverse(data.begin(), data.end()); + + is_equal = std::equal(rdata.begin(), + rdata.end(), + data.begin()); + + CHECK(!is_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair_virtual) + { + Data data(initial_data.begin(), initial_data.end()); + + char buffer[sizeof(Data)]; + + memcpy(&buffer, &data, sizeof(data)); + + IData& idata(*reinterpret_cast(buffer)); + idata.repair(); + + // Check that the memcpy'd vector is the same. + CHECK_EQUAL(data.size(), idata.size()); + CHECK(!idata.empty()); + CHECK(idata.full()); + + bool is_equal = std::equal(idata.begin(), + idata.end(), + data.begin()); + + CHECK(is_equal); + + // Modify the original and check that the memcpy'd vector is not the same. + std::reverse(data.begin(), data.end()); + + is_equal = std::equal(idata.begin(), + idata.end(), + data.begin()); + + CHECK(!is_equal); + } }; } diff --git a/test/test_vector_pointer.cpp b/test/test_vector_pointer.cpp index 5f08cbe5..232faa21 100644 --- a/test/test_vector_pointer.cpp +++ b/test/test_vector_pointer.cpp @@ -31,11 +31,12 @@ SOFTWARE. #include #include #include +#include #include "vector.h" namespace -{ +{ SUITE(test_vector_pointer) { static const size_t SIZE = 10; @@ -159,7 +160,7 @@ namespace Data data(initial_data.begin(), initial_data.end()); Data data2(data); CHECK(data2 == data); - + data2[2] = nullptr; CHECK(data2 != data); } @@ -612,7 +613,7 @@ namespace const size_t INITIAL_SIZE = 5; const size_t INSERT_SIZE = 3; int INITIAL_VALUE = 11; - + for (size_t offset = 0; offset <= INITIAL_SIZE; ++offset) { Compare_Data compare_data; @@ -700,7 +701,7 @@ namespace offset = 4; CHECK_THROW(data.insert(data.begin() + offset, initial_data.begin(), initial_data.end()), etl::vector_full); - + offset = data.size(); CHECK_THROW(data.insert(data.begin() + offset, initial_data.begin(), initial_data.end()), etl::vector_full); @@ -740,7 +741,7 @@ namespace CHECK(is_equal); } - + //************************************************************************* TEST_FIXTURE(SetupFixture, test_clear) { @@ -933,5 +934,38 @@ namespace const Data initial2(initial_data.begin(), initial_data.end()); CHECK((initial >= initial2) == (initial_data >= initial_data)); } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_memcpy_repair) + { + Data data(initial_data.begin(), initial_data.end()); + + char buffer[sizeof(Data)]; + + memcpy(&buffer, &data, sizeof(data)); + + Data& rdata(*reinterpret_cast(buffer)); + rdata.repair(); + + // Check that the memcpy'd vector is the same. + CHECK_EQUAL(data.size(), rdata.size()); + CHECK(!rdata.empty()); + CHECK(rdata.full()); + + bool is_equal = std::equal(rdata.begin(), + rdata.end(), + data.begin()); + + CHECK(is_equal); + + // Modify the original and check that the memcpy'd vector is not the same. + std::reverse(data.begin(), data.end()); + + is_equal = std::equal(rdata.begin(), + rdata.end(), + data.begin()); + + CHECK(!is_equal); + } }; } diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index d38c27c9..6015dc55 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -72,7 +72,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;ETL_THROW_EXCEPTIONS;ETL_VERBOSE_ERRORS;ETL_CHECK_PUSH_POP;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;ETL_IVECTOR_REPAIR_ENABLE;ETL_ISTRING_REPAIR_ENABLE;ETL_IDEQUE_REPAIR_ENABLE;ETL_IN_UNIT_TEST;ETL_THROW_EXCEPTIONS;ETL_VERBOSE_ERRORS;ETL_CHECK_PUSH_POP;%(PreprocessorDefinitions) ../../unittest-cpp/UnitTest++/;../../src @@ -91,7 +91,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;ETL_THROW_EXCEPTIONS;ETL_VERBOSE_ERRORS;ETL_CHECK_PUSH_POP;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;ETL_IN_UNIT_TEST;ETL_THROW_EXCEPTIONS;ETL_VERBOSE_ERRORS;ETL_CHECK_PUSH_POP;%(PreprocessorDefinitions) ../../unittest-cpp/UnitTest++/;../../src diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 6be3e083..f2bf51fa 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -37,6 +37,9 @@ {7028012c-30c4-4993-b2d9-3b1521a610ae} + + {5de50c3d-4679-4eb3-9b76-e43e1aad6a66} + @@ -336,12 +339,6 @@ ETL\Utilities - - ETL\Patterns - - - ETL\Patterns - ETL\Containers @@ -441,6 +438,12 @@ Header Files + + Header Files + + + Header Files + @@ -773,7 +776,7 @@ Resource Files - ETL\Patterns + Source Files