From 4c713d38f01276e7c9a6e184e69bde32dca7929a Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 2 Feb 2019 19:37:23 +0000 Subject: [PATCH 1/5] Callback service --- include/etl/callback_service.h | 164 ++++++++++++++++ test/codeblocks/ETL.cbp | 2 + test/test_callback_service.cpp | 320 ++++++++++++++++++++++++++++++++ test/vs2017/etl.vcxproj | 2 + test/vs2017/etl.vcxproj.filters | 6 + 5 files changed, 494 insertions(+) create mode 100644 include/etl/callback_service.h create mode 100644 test/test_callback_service.cpp diff --git a/include/etl/callback_service.h b/include/etl/callback_service.h new file mode 100644 index 00000000..e7c207fe --- /dev/null +++ b/include/etl/callback_service.h @@ -0,0 +1,164 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 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_CALLBACK_SERVICE_INCLUDED +#define ETL_CALLBACK_SERVICE_INCLUDED + +#include "nullptr.h" +#include "static_assert.h" +#include "function.h" + +namespace etl +{ + //*************************************************************************** + /// An indexed callback service. + /// \tparam NUMBER_OF_CALLBACKS The number of callbacks to handle. + /// The callback ids must range between 0 and NUMBER_OF_CALLBACKS - 1. + //*************************************************************************** + template + class callback_service + { + public: + + //************************************************************************* + /// Reset the callback service. + /// Sets all callbacks to the internal default. + //************************************************************************* + static void initialize() + { + for (size_t i = 0; i < NUMBER_OF_CALLBACKS; ++i) + { + lookup[i] = &unhandled_callback; + } + + p_unhandled = nullptr; + } + + //************************************************************************* + /// Registers a callback for the specified id. + /// Compile time assert if the id is out of range. + /// \tparam ID The id of the callback. + /// \param callback reference to the callback. + //************************************************************************* + template + static void register_callback(etl::ifunction& callback) + { + ETL_STATIC_ASSERT(ID < NUMBER_OF_CALLBACKS, "Callback Id out of range"); + + lookup[ID] = &callback; + } + + //************************************************************************* + /// Registers a callback for the specified id. + /// No action if the id is out of range. + /// \param id Id of the callback. + /// \param callback Reference to the callback. + //************************************************************************* + static void register_callback(const size_t id, etl::ifunction& callback) + { + if (id < NUMBER_OF_CALLBACKS) + { + lookup[id] = &callback; + } + } + + //************************************************************************* + /// Executes the callback function for the index. + /// Compile time assert if the id is out of range. + /// \tparam ID The id of the callback. + //************************************************************************* + template + static void callback() + { + ETL_STATIC_ASSERT(ID < NUMBER_OF_CALLBACKS, "Callback Id out of range"); + + (*lookup[ID])(ID); + } + + //************************************************************************* + /// Executes the callback function for the index. + /// \param id Id of the callback. + //************************************************************************* + static void callback(const size_t id) + { + if (id < NUMBER_OF_CALLBACKS) + { + (*lookup[id])(id); + } + else + { + unhandled(id); + } + } + + //************************************************************************* + /// Registers an alternative callback for unhandled ids. + /// \param callback A reference to the user supplied 'unhandled' callback. + //************************************************************************* + static void register_unhandled_callback(etl::ifunction& callback) + { + p_unhandled = &callback; + } + + private: + + //************************************************************************* + /// The default callback function. + /// Does nothing. + //************************************************************************* + static void unhandled(size_t id) + { + if (p_unhandled != nullptr) + { + (*p_unhandled)(id); + } + } + + /// The default callback for unhandled ids. + static etl::function_fp::unhandled> unhandled_callback; + + /// Pointer to the user defined 'unhandled' callback. + static etl::ifunction* p_unhandled; + + /// Lookup table of callbacks. + static etl::ifunction* lookup[NUMBER_OF_CALLBACKS]; + }; + + template + etl::function_fp::unhandled> etl::callback_service::unhandled_callback; + + template + etl::ifunction* etl::callback_service::lookup[NUMBER_OF_CALLBACKS]; + + template + etl::ifunction* etl::callback_service::p_unhandled = nullptr; +} + +#endif diff --git a/test/codeblocks/ETL.cbp b/test/codeblocks/ETL.cbp index b07b8954..c573df20 100644 --- a/test/codeblocks/ETL.cbp +++ b/test/codeblocks/ETL.cbp @@ -161,6 +161,7 @@ + @@ -341,6 +342,7 @@ + diff --git a/test/test_callback_service.cpp b/test/test_callback_service.cpp new file mode 100644 index 00000000..acb9ca40 --- /dev/null +++ b/test/test_callback_service.cpp @@ -0,0 +1,320 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 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 "etl/function.h" +#include "etl/callback_service.h" + +namespace +{ + typedef etl::callback_service<3> Service; + + //***************************************************************************** + bool global_called = false; + bool member1_called = false; + bool member2_called = false; + bool unhandled_called = false; + size_t called_id = UINT_MAX; + + //***************************************************************************** + // The global function taking no parameters. + //***************************************************************************** + void global(size_t id) + { + global_called = true; + called_id = id; + } + + //***************************************************************************** + // The external unhandled callback. + //***************************************************************************** + void unhandled(size_t id) + { + unhandled_called = true; + called_id = id; + } + + //***************************************************************************** + // The test class with member functions. + //***************************************************************************** + class Test + { + public: + + Test() + : callback(*this) + { + } + + void member1(size_t id) + { + member1_called = true; + called_id = id; + } + + void member2(size_t id) + { + member2_called = true; + called_id = id; + } + + // Callback for 'member1'. + etl::function_mp callback; + }; + + Test test; + + // Callback for 'member2'. + etl::function_imp member_callback; + + // Callback for 'global'. + etl::function_fp global_callback; + + // Callback for 'unhandled'. + etl::function_fp unhandled_callback; +} + +//***************************************************************************** +// Initialises the test results. +//***************************************************************************** +struct SetupFixture +{ + SetupFixture() + { + called_id = UINT_MAX; + global_called = false; + member1_called = false; + member2_called = false; + unhandled_called = false; + } +}; + +namespace +{ + enum + { + GLOBAL, + MEMBER1, + MEMBER2, + UNHANDLED, + OUT_OF_RANGE + }; + + SUITE(test_callback_service) + { + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_global_compile_time) + { + Service::initialize(); + Service::register_callback(global_callback); + Service::register_callback(test.callback); + Service::register_callback(member_callback); + + Service::callback(); + + CHECK_EQUAL(GLOBAL, called_id); + CHECK(global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_global_run_time) + { + Service::initialize(); + Service::register_callback(GLOBAL, global_callback); + Service::register_callback(MEMBER1, test.callback); + Service::register_callback(MEMBER2, member_callback); + + Service::callback(GLOBAL); + + CHECK_EQUAL(GLOBAL, called_id); + CHECK(global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_member1_compile_time) + { + Service::initialize(); + Service::register_callback(global_callback); + Service::register_callback(test.callback); + Service::register_callback(member_callback); + + Service::callback(); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_member1_run_time) + { + Service::initialize(); + Service::register_callback(GLOBAL, global_callback); + Service::register_callback(MEMBER1, test.callback); + Service::register_callback(MEMBER2, member_callback); + + Service::callback(MEMBER1); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_member2_compile_time) + { + Service::initialize(); + Service::register_callback(global_callback); + Service::register_callback(test.callback); + Service::register_callback(member_callback); + + Service::callback(); + + CHECK_EQUAL(MEMBER2, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_out_of_range_run_time_default) + { + Service::initialize(); + Service::register_callback(global_callback); + Service::register_callback(test.callback); + Service::register_callback(member_callback); + + Service::callback(OUT_OF_RANGE); + + CHECK_EQUAL(UINT_MAX, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_out_of_range_run_time_user_supplied) + { + Service::initialize(); + Service::register_callback(global_callback); + Service::register_callback(test.callback); + Service::register_callback(member_callback); + + Service::register_unhandled_callback(unhandled_callback); + + Service::callback(OUT_OF_RANGE); + + CHECK_EQUAL(OUT_OF_RANGE, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_not_registered_compile_time_default) + { + Service::initialize(); + Service::register_callback(global_callback); + Service::register_callback(member_callback); + + Service::callback(); + + CHECK_EQUAL(UINT_MAX, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_not_registered_run_time_default) + { + Service::initialize(); + Service::register_callback(GLOBAL, global_callback); + Service::register_callback(MEMBER2, member_callback); + + Service::callback(MEMBER1); + + CHECK_EQUAL(UINT_MAX, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_not_registered_compile_time_user_supplied) + { + Service::initialize(); + Service::register_callback(global_callback); + Service::register_callback(member_callback); + + Service::register_unhandled_callback(unhandled_callback); + + Service::callback(); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_run_time_user_supplied) + { + Service::initialize(); + Service::register_callback(GLOBAL, global_callback); + Service::register_callback(MEMBER2, member_callback); + + Service::register_unhandled_callback(unhandled_callback); + + Service::callback(MEMBER1); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(unhandled_called); + } + }; +} diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index 6679be4f..d1662b17 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -373,6 +373,7 @@ + @@ -565,6 +566,7 @@ + diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 161a765b..888dafde 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -723,6 +723,9 @@ Source Files + + ETL\Frameworks + @@ -1145,6 +1148,9 @@ Source Files + + Source Files + From 2cef994d5b2ccca3e23c25ea56d943d740dfd04b Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 3 Feb 2019 10:24:47 +0000 Subject: [PATCH 2/5] Changed from static to normal class --- include/etl/callback_service.h | 39 ++++------- test/test_callback_service.cpp | 119 ++++++++++++++++++--------------- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/include/etl/callback_service.h b/include/etl/callback_service.h index e7c207fe..d7122030 100644 --- a/include/etl/callback_service.h +++ b/include/etl/callback_service.h @@ -34,6 +34,7 @@ SOFTWARE. #include "nullptr.h" #include "static_assert.h" #include "function.h" +#include "array.h" namespace etl { @@ -51,14 +52,11 @@ namespace etl /// Reset the callback service. /// Sets all callbacks to the internal default. //************************************************************************* - static void initialize() + callback_service() + : unhandled_callback(*this, &callback_service::unhandled), + p_unhandled(nullptr) { - for (size_t i = 0; i < NUMBER_OF_CALLBACKS; ++i) - { - lookup[i] = &unhandled_callback; - } - - p_unhandled = nullptr; + lookup.fill(&unhandled_callback); } //************************************************************************* @@ -68,7 +66,7 @@ namespace etl /// \param callback reference to the callback. //************************************************************************* template - static void register_callback(etl::ifunction& callback) + void register_callback(etl::ifunction& callback) { ETL_STATIC_ASSERT(ID < NUMBER_OF_CALLBACKS, "Callback Id out of range"); @@ -81,7 +79,7 @@ namespace etl /// \param id Id of the callback. /// \param callback Reference to the callback. //************************************************************************* - static void register_callback(const size_t id, etl::ifunction& callback) + void register_callback(const size_t id, etl::ifunction& callback) { if (id < NUMBER_OF_CALLBACKS) { @@ -95,7 +93,7 @@ namespace etl /// \tparam ID The id of the callback. //************************************************************************* template - static void callback() + void callback() { ETL_STATIC_ASSERT(ID < NUMBER_OF_CALLBACKS, "Callback Id out of range"); @@ -106,7 +104,7 @@ namespace etl /// Executes the callback function for the index. /// \param id Id of the callback. //************************************************************************* - static void callback(const size_t id) + void callback(const size_t id) { if (id < NUMBER_OF_CALLBACKS) { @@ -122,7 +120,7 @@ namespace etl /// Registers an alternative callback for unhandled ids. /// \param callback A reference to the user supplied 'unhandled' callback. //************************************************************************* - static void register_unhandled_callback(etl::ifunction& callback) + void register_unhandled_callback(etl::ifunction& callback) { p_unhandled = &callback; } @@ -133,7 +131,7 @@ namespace etl /// The default callback function. /// Does nothing. //************************************************************************* - static void unhandled(size_t id) + void unhandled(size_t id) { if (p_unhandled != nullptr) { @@ -142,23 +140,14 @@ namespace etl } /// The default callback for unhandled ids. - static etl::function_fp::unhandled> unhandled_callback; + etl::function, size_t> unhandled_callback; /// Pointer to the user defined 'unhandled' callback. - static etl::ifunction* p_unhandled; + etl::ifunction* p_unhandled; /// Lookup table of callbacks. - static etl::ifunction* lookup[NUMBER_OF_CALLBACKS]; + etl::array*, NUMBER_OF_CALLBACKS> lookup; }; - - template - etl::function_fp::unhandled> etl::callback_service::unhandled_callback; - - template - etl::ifunction* etl::callback_service::lookup[NUMBER_OF_CALLBACKS]; - - template - etl::ifunction* etl::callback_service::p_unhandled = nullptr; } #endif diff --git a/test/test_callback_service.cpp b/test/test_callback_service.cpp index acb9ca40..900c4fae 100644 --- a/test/test_callback_service.cpp +++ b/test/test_callback_service.cpp @@ -131,12 +131,13 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_global_compile_time) { - Service::initialize(); - Service::register_callback(global_callback); - Service::register_callback(test.callback); - Service::register_callback(member_callback); + Service service; - Service::callback(); + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(); CHECK_EQUAL(GLOBAL, called_id); CHECK(global_called); @@ -148,12 +149,13 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_global_run_time) { - Service::initialize(); - Service::register_callback(GLOBAL, global_callback); - Service::register_callback(MEMBER1, test.callback); - Service::register_callback(MEMBER2, member_callback); + Service service; - Service::callback(GLOBAL); + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER1, test.callback); + service.register_callback(MEMBER2, member_callback); + + service.callback(GLOBAL); CHECK_EQUAL(GLOBAL, called_id); CHECK(global_called); @@ -165,12 +167,13 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_member1_compile_time) { - Service::initialize(); - Service::register_callback(global_callback); - Service::register_callback(test.callback); - Service::register_callback(member_callback); + Service service; - Service::callback(); + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(); CHECK_EQUAL(MEMBER1, called_id); CHECK(!global_called); @@ -182,12 +185,13 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_member1_run_time) { - Service::initialize(); - Service::register_callback(GLOBAL, global_callback); - Service::register_callback(MEMBER1, test.callback); - Service::register_callback(MEMBER2, member_callback); + Service service; - Service::callback(MEMBER1); + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER1, test.callback); + service.register_callback(MEMBER2, member_callback); + + service.callback(MEMBER1); CHECK_EQUAL(MEMBER1, called_id); CHECK(!global_called); @@ -199,12 +203,13 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_member2_compile_time) { - Service::initialize(); - Service::register_callback(global_callback); - Service::register_callback(test.callback); - Service::register_callback(member_callback); + Service service; - Service::callback(); + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(); CHECK_EQUAL(MEMBER2, called_id); CHECK(!global_called); @@ -216,12 +221,13 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_unhandled_out_of_range_run_time_default) { - Service::initialize(); - Service::register_callback(global_callback); - Service::register_callback(test.callback); - Service::register_callback(member_callback); + Service service; - Service::callback(OUT_OF_RANGE); + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(OUT_OF_RANGE); CHECK_EQUAL(UINT_MAX, called_id); CHECK(!global_called); @@ -233,14 +239,15 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_unhandled_out_of_range_run_time_user_supplied) { - Service::initialize(); - Service::register_callback(global_callback); - Service::register_callback(test.callback); - Service::register_callback(member_callback); + Service service; - Service::register_unhandled_callback(unhandled_callback); + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); - Service::callback(OUT_OF_RANGE); + service.register_unhandled_callback(unhandled_callback); + + service.callback(OUT_OF_RANGE); CHECK_EQUAL(OUT_OF_RANGE, called_id); CHECK(!global_called); @@ -252,11 +259,12 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_unhandled_not_registered_compile_time_default) { - Service::initialize(); - Service::register_callback(global_callback); - Service::register_callback(member_callback); + Service service; - Service::callback(); + service.register_callback(global_callback); + service.register_callback(member_callback); + + service.callback(); CHECK_EQUAL(UINT_MAX, called_id); CHECK(!global_called); @@ -268,11 +276,12 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_unhandled_not_registered_run_time_default) { - Service::initialize(); - Service::register_callback(GLOBAL, global_callback); - Service::register_callback(MEMBER2, member_callback); + Service service; - Service::callback(MEMBER1); + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER2, member_callback); + + service.callback(MEMBER1); CHECK_EQUAL(UINT_MAX, called_id); CHECK(!global_called); @@ -284,13 +293,14 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_not_registered_compile_time_user_supplied) { - Service::initialize(); - Service::register_callback(global_callback); - Service::register_callback(member_callback); + Service service; - Service::register_unhandled_callback(unhandled_callback); + service.register_callback(global_callback); + service.register_callback(member_callback); - Service::callback(); + service.register_unhandled_callback(unhandled_callback); + + service.callback(); CHECK_EQUAL(MEMBER1, called_id); CHECK(!global_called); @@ -302,13 +312,14 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_callback_unhandled_run_time_user_supplied) { - Service::initialize(); - Service::register_callback(GLOBAL, global_callback); - Service::register_callback(MEMBER2, member_callback); + Service service; - Service::register_unhandled_callback(unhandled_callback); + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER2, member_callback); - Service::callback(MEMBER1); + service.register_unhandled_callback(unhandled_callback); + + service.callback(MEMBER1); CHECK_EQUAL(MEMBER1, called_id); CHECK(!global_called); From df56f94815e01c401280bae44946dafd4e0511d0 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 3 Feb 2019 20:29:40 +0000 Subject: [PATCH 3/5] Added OFFSET template parameter --- .../FunctionInterruptSimulation.cpp | 142 +++++++++--------- .../FunctionInterruptSimulation.vcxproj | 6 +- include/etl/callback_service.h | 84 ++++++----- test/test_callback_service.cpp | 8 +- 4 files changed, 124 insertions(+), 116 deletions(-) diff --git a/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp b/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp index 5f0adb99..d7fffbdf 100644 --- a/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp +++ b/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp @@ -1,58 +1,61 @@ #include -#include -#include "function.h" +#include "etl/function.h" +#include "etl/callback_service.h" -//******************************** -// Fake UART Rx register. -//******************************** -char get_char() +enum VectorId { - static char c = 'A'; - return c++; -} + TIM1_CC_IRQ_HANDLER = 42, + TIM2_IRQ_HANDLER = 43, + TIM3_IRQ_HANDLER = 44, + USART1_IRQ_HANDLER = 52, + USART2_IRQ_HANDLER = 53, + VECTOR_ID_RANGE = USART2_IRQ_HANDLER - TIM1_CC_IRQ_HANDLER + 1, + VECTOR_ID_OFFSET = TIM1_CC_IRQ_HANDLER +}; -//******************************** -// Interrupt vectors & callbacks. -//******************************** -// Callback interfaces. -// Note that they do not require any knowledge about the callee apart from the parameter type. -etl::ifunction* timer1_callback; // A pointer to a callback taking no parameters. -etl::ifunction* timer2_callback; // A pointer to a callback taking no parameters. -etl::ifunction* timer3_callback; // A pointer to a callback taking no parameters. -etl::ifunction* uart1_rx_callback; // A pointer to a callback taking a char parameter. -etl::ifunction* uart2_rx_callback; // A pointer to a callback taking a char parameter. +typedef etl::callback_service InterruptVectors; + +// Ensure that the callback service is initialised before use. +InterruptVectors& GetInterruptVectorsInstance() +{ + static InterruptVectors interruptVectors; + + return interruptVectors; +} extern "C" { + InterruptVectors& interruptVectors = GetInterruptVectorsInstance(); + // Function called from the timer1 interrupt vector. - void Timer1Interrupt() + void TIM1_CC_IRQHandler() { - (*timer1_callback)(); + interruptVectors.callback(); } // Function called from the timer2 interrupt vector. - void Timer2Interrupt() + void TIM2_IRQHandler() { - (*timer2_callback)(); + interruptVectors.callback(); } // Function called from the timer3 interrupt vector. - void Timer3Interrupt() + void TIM3_IRQHandler() { - (*timer3_callback)(); + interruptVectors.callback(); } - // Function called from the UART1 rx interrupt vector. - void Uart1RxInterrupt() + // Function called from the usart1 interrupt vector. + void USART1_IRQHandler() { - (*uart1_rx_callback)(get_char()); + interruptVectors.callback(); } - // Function called from the UART2 rx interrupt vector. - void Uart2RxInterrupt() + // Function called from the usart2 interrupt vector. + void USART2_IRQHandler() { - (*uart2_rx_callback)(get_char()); + interruptVectors.callback(); } } @@ -63,34 +66,29 @@ class Timer { public: - // Constructor. - Timer() + Timer(int interruptId) + : callback(*this) { + GetInterruptVectorsInstance().register_callback(interruptId, callback); } // Handler for interrupts from the timer. - void MemberTimerInterruptHandler() + void InterruptHandler(const size_t id) { - std::cout << "Timer interrupt (member)\n"; + std::cout << "Timer interrupt (member) : ID " << id << "\n"; } - // Static handler for interrupts from the timer. - static void StaticTimerInterruptHandler() - { - std::cout << "Timer interrupt (static)\n"; - } + etl::function_mp callback; }; //******************************** // Free function timer driver. //******************************** -void FreeTimerInterruptHandler() +void FreeTimerInterruptHandler(const size_t id) { - std::cout << "Timer interrupt (free)\n"; + std::cout << "Timer interrupt (free) : ID " << id << "\n"; } -etl::function_fv free_callback; - //******************************** // UART driver. //******************************** @@ -99,29 +97,37 @@ class Uart public: // Constructor. - Uart(int port_id) - : port_id(port_id) + Uart(int port_id, int interruptId) + : port_id(port_id), + callback(*this) { + GetInterruptVectorsInstance().register_callback(interruptId, callback); } - // Handler for rx interrupts from the UART. - void RxInterruptHandler(char c) + // Handler for interrupts from the UART. + void InterruptHandler(const size_t id) { - std::cout << "UART" << port_id << " Rx char interrupt : Received '" << c << "'\n"; + std::cout << "UART" << port_id << " : ID " << id << "\n"; } + etl::function_mp callback; + int port_id; }; -// Declare the driver instances. -Timer timer; -Uart uart1(0); -Uart uart2(1); +void UnhandledInterrupt(const size_t id) +{ + std::cout << "Unhandled Interrupt : ID " << id << "\n"; +} -etl::function_imv timer_member_callback; -etl::function_fv<&Timer::StaticTimerInterruptHandler> timer_static_callback; -etl::function_imp uart1_callback; -etl::function_imp uart2_callback; +// Declare the driver instances. +Timer timer(TIM1_CC_IRQ_HANDLER); +Uart uart1(0, USART1_IRQ_HANDLER); +Uart uart2(1, USART2_IRQ_HANDLER); + +// Declare the callbacks for the free functions. +etl::function_fp timer_free_callback; +etl::function_fp unhandled_callback; //******************************** // Test it out. @@ -129,21 +135,17 @@ etl::function_imp uart2_callb int main() { // Setup the callbacks. - // This may be part of the cross platform interface. - timer1_callback = &timer_member_callback; - timer2_callback = &timer_static_callback; - timer3_callback = &free_callback; - uart1_rx_callback = &uart1_callback; - uart2_rx_callback = &uart2_callback; + InterruptVectors& interruptVectors = GetInterruptVectorsInstance(); + + interruptVectors.register_callback(timer_free_callback); + interruptVectors.register_unhandled_callback(unhandled_callback); // Simulate the interrupts. - Timer1Interrupt(); - Timer2Interrupt(); - Timer3Interrupt(); - Uart1RxInterrupt(); - Uart2RxInterrupt(); + TIM1_CC_IRQHandler(); + TIM2_IRQHandler(); + USART1_IRQHandler(); + USART2_IRQHandler(); + TIM3_IRQHandler(); // Unhandled! return 0; } - - diff --git a/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj b/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj index 3c2ce147..ca2c92e1 100644 --- a/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj +++ b/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj @@ -29,7 +29,7 @@ {5157DB15-C255-4E47-9FB1-AF388437F90F} Win32Proj FunctionInterruptSimulation - 10.0.15063.0 + 10.0.17763.0 @@ -104,12 +104,12 @@ - Use + NotUsing Level3 Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ../../../src + ../../../include;%(AdditionalIncludeDirectories) Console diff --git a/include/etl/callback_service.h b/include/etl/callback_service.h index d7122030..effdc53c 100644 --- a/include/etl/callback_service.h +++ b/include/etl/callback_service.h @@ -31,6 +31,7 @@ SOFTWARE. #ifndef ETL_CALLBACK_SERVICE_INCLUDED #define ETL_CALLBACK_SERVICE_INCLUDED +#include "platform.h" #include "nullptr.h" #include "static_assert.h" #include "function.h" @@ -40,10 +41,11 @@ namespace etl { //*************************************************************************** /// An indexed callback service. - /// \tparam NUMBER_OF_CALLBACKS The number of callbacks to handle. - /// The callback ids must range between 0 and NUMBER_OF_CALLBACKS - 1. + /// \tparam RANGE The number of callbacks to handle. + /// \tparam OFFSET The lowest callback id value. + /// The callback ids must range between OFFSET and OFFSET + RANGE - 1. //*************************************************************************** - template + template class callback_service { public: @@ -53,7 +55,7 @@ namespace etl /// Sets all callbacks to the internal default. //************************************************************************* callback_service() - : unhandled_callback(*this, &callback_service::unhandled), + : unhandled_callback(*this, &callback_service::unhandled), p_unhandled(nullptr) { lookup.fill(&unhandled_callback); @@ -63,14 +65,15 @@ namespace etl /// Registers a callback for the specified id. /// Compile time assert if the id is out of range. /// \tparam ID The id of the callback. - /// \param callback reference to the callback. + /// \param callback Reference to the callback. //************************************************************************* template void register_callback(etl::ifunction& callback) { - ETL_STATIC_ASSERT(ID < NUMBER_OF_CALLBACKS, "Callback Id out of range"); + ETL_STATIC_ASSERT(ID < (OFFSET + RANGE), "Callback Id out of range"); + ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); - lookup[ID] = &callback; + lookup[ID - OFFSET] = &callback; } //************************************************************************* @@ -81,38 +84,9 @@ namespace etl //************************************************************************* void register_callback(const size_t id, etl::ifunction& callback) { - if (id < NUMBER_OF_CALLBACKS) + if ((id >= OFFSET) && (id < (OFFSET + RANGE))) { - lookup[id] = &callback; - } - } - - //************************************************************************* - /// Executes the callback function for the index. - /// Compile time assert if the id is out of range. - /// \tparam ID The id of the callback. - //************************************************************************* - template - void callback() - { - ETL_STATIC_ASSERT(ID < NUMBER_OF_CALLBACKS, "Callback Id out of range"); - - (*lookup[ID])(ID); - } - - //************************************************************************* - /// Executes the callback function for the index. - /// \param id Id of the callback. - //************************************************************************* - void callback(const size_t id) - { - if (id < NUMBER_OF_CALLBACKS) - { - (*lookup[id])(id); - } - else - { - unhandled(id); + lookup[id - OFFSET] = &callback; } } @@ -125,6 +99,36 @@ namespace etl p_unhandled = &callback; } + //************************************************************************* + /// Executes the callback function for the index. + /// Compile time assert if the id is out of range. + /// \tparam ID The id of the callback. + //************************************************************************* + template + void callback() + { + ETL_STATIC_ASSERT(ID < (OFFSET + RANGE), "Callback Id out of range"); + ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); + + (*lookup[ID - OFFSET])(ID); + } + + //************************************************************************* + /// Executes the callback function for the index. + /// \param id Id of the callback. + //************************************************************************* + void callback(const size_t id) + { + if ((id >= OFFSET) && (id < (OFFSET + RANGE))) + { + (*lookup[id - OFFSET])(id); + } + else + { + unhandled(id); + } + } + private: //************************************************************************* @@ -140,13 +144,13 @@ namespace etl } /// The default callback for unhandled ids. - etl::function, size_t> unhandled_callback; + etl::function, size_t> unhandled_callback; /// Pointer to the user defined 'unhandled' callback. etl::ifunction* p_unhandled; /// Lookup table of callbacks. - etl::array*, NUMBER_OF_CALLBACKS> lookup; + etl::array*, RANGE> lookup; }; } diff --git a/test/test_callback_service.cpp b/test/test_callback_service.cpp index 900c4fae..67de9b16 100644 --- a/test/test_callback_service.cpp +++ b/test/test_callback_service.cpp @@ -33,7 +33,10 @@ SOFTWARE. namespace { - typedef etl::callback_service<3> Service; + const size_t SIZE = 3U; + const size_t OFFSET = 5U; + + typedef etl::callback_service Service; //***************************************************************************** bool global_called = false; @@ -119,10 +122,9 @@ namespace { enum { - GLOBAL, + GLOBAL = OFFSET, MEMBER1, MEMBER2, - UNHANDLED, OUT_OF_RANGE }; From 65e8a981e3afcde583059c4167fefffbab48a3ec Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 3 Feb 2019 20:31:14 +0000 Subject: [PATCH 4/5] Added const to template parameter --- include/etl/callback_service.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/etl/callback_service.h b/include/etl/callback_service.h index effdc53c..4fe608f2 100644 --- a/include/etl/callback_service.h +++ b/include/etl/callback_service.h @@ -45,7 +45,7 @@ namespace etl /// \tparam OFFSET The lowest callback id value. /// The callback ids must range between OFFSET and OFFSET + RANGE - 1. //*************************************************************************** - template + template class callback_service { public: From 047398f8382f741c33c8789ee7622edb9ca095ef Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 4 Feb 2019 21:06:30 +0000 Subject: [PATCH 5/5] Version & release notes. Optimised internal 'unhandled' declaration. --- include/etl/callback_service.h | 12 +++++++----- include/etl/version.h | 4 ++-- support/Release notes.txt | 5 +++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/etl/callback_service.h b/include/etl/callback_service.h index 4fe608f2..cd098d5d 100644 --- a/include/etl/callback_service.h +++ b/include/etl/callback_service.h @@ -55,7 +55,7 @@ namespace etl /// Sets all callbacks to the internal default. //************************************************************************* callback_service() - : unhandled_callback(*this, &callback_service::unhandled), + : unhandled_callback(*this), p_unhandled(nullptr) { lookup.fill(&unhandled_callback); @@ -71,7 +71,7 @@ namespace etl void register_callback(etl::ifunction& callback) { ETL_STATIC_ASSERT(ID < (OFFSET + RANGE), "Callback Id out of range"); - ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); + ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); lookup[ID - OFFSET] = &callback; } @@ -108,7 +108,7 @@ namespace etl void callback() { ETL_STATIC_ASSERT(ID < (OFFSET + RANGE), "Callback Id out of range"); - ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); + ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); (*lookup[ID - OFFSET])(ID); } @@ -133,7 +133,7 @@ namespace etl //************************************************************************* /// The default callback function. - /// Does nothing. + /// Calls the user defined 'unhandled' callback if it exists. //************************************************************************* void unhandled(size_t id) { @@ -144,7 +144,9 @@ namespace etl } /// The default callback for unhandled ids. - etl::function, size_t> unhandled_callback; + etl::function_mp, + size_t, + &callback_service::unhandled> unhandled_callback; /// Pointer to the user defined 'unhandled' callback. etl::ifunction* p_unhandled; diff --git a/include/etl/version.h b/include/etl/version.h index 868c269e..ef318fff 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -38,8 +38,8 @@ SOFTWARE. ///\ingroup utilities #define ETL_VERSION_MAJOR 14 -#define ETL_VERSION_MINOR 7 -#define ETL_VERSION_PATCH 2 +#define ETL_VERSION_MINOR 8 +#define ETL_VERSION_PATCH 0 #define ETL_VERSION ETL_STRINGIFY(ETL_VERSION_MAJOR) ETL_STRINGIFY(ETL_VERSION_MINOR) ETL_STRINGIFY(ETL_VERSION_PATCH) #define ETL_VERSION_W ETL_WIDE_STRING(ETL_CONCAT(ETL_CONCAT(ETL_VERSION_MAJOR, ETL_VERSION_MINOR), ETL_VERSION_PATCH)) diff --git a/support/Release notes.txt b/support/Release notes.txt index 1968df1f..a9ad0685 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,8 @@ +=============================================================================== +14.8.0 +Added etl::callback_service +Useful for abstracting the interrupt system from C++ handlers. + =============================================================================== 14.7.2 Added #include to message_router.h for improved cross platform compatibility.