diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 51a009e7..d6733308 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -108,4 +108,5 @@ SOFTWARE. #define ETL_SPAN_FILE_ID "75" #define ETL_ALGORITHM_FILE_ID "76" #define ETL_NOT_NULL_FILE_ID "77" +#define ETL_SIGNAL_FILE_ID "78" #endif diff --git a/include/etl/signal.h b/include/etl/signal.h new file mode 100644 index 00000000..5278cc04 --- /dev/null +++ b/include/etl/signal.h @@ -0,0 +1,380 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2025 John Wellbelove, Mark Kitson + +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_SIGNAL_INCLUDED +#define ETL_SIGNAL_INCLUDED + +#include + +#include "platform.h" + +#if ETL_NOT_USING_CPP11 && !defined(ETL_IN_UNIT_TEST) +#error NOT SUPPORTED FOR C++03 OR BELOW +#endif + +#if ETL_USING_CPP11 + +#include "exception.h" +#include "error_handler.h" +#include "delegate.h" +#include "algorithm.h" +#include "iterator.h" +#include "type_traits.h" +#include "initializer_list.h" +#include "span.h" +#include "file_error_numbers.h" + +//***************************************************************************** +///\defgroup signal signal +/// Container that handles storing and calling callbacks. +///\ingroup containers +//***************************************************************************** + +namespace etl +{ + //*************************************************************************** + ///\ingroup signal + /// The base class for signal exceptions. + //*************************************************************************** + class signal_exception : public exception + { + public: + signal_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception{reason_, file_name_, line_number_} + { + } + }; + + //*************************************************************************** + ///\ingroup signal + /// Signal full exception. + //*************************************************************************** + class signal_full : public signal_exception + { + public: + signal_full(string_type file_name_, numeric_type line_number_) + : signal_exception{ETL_ERROR_TEXT("signal:full", ETL_SIGNAL_FILE_ID"A"), file_name_, line_number_} + { + } + }; + + //*************************************************************************** + ///\ingroup signal + /// + ///\brief A lightweight signal class designed for efficient memory usage and + /// ability to store in ROM. + /// + ///\tparam TFunction: Callback signature. + ///\tparam Size: Maximum number of slots that can be connected to the signal. + ///\tparam TSlot: Function-object type or container type that can be invoked. Default etl::delegate. + //*************************************************************************** + template > + class signal + { + public: + + using slot_type = TSlot; + using size_type = size_t; + using span_type = etl::span; + + //************************************************************************* + ///\brief Construct the signal from a variadic list of slots. + /// + ///\param slots: Variadic list of slots. + //************************************************************************* + template + ETL_CONSTEXPR14 explicit signal(TSlots&&... slots) ETL_NOEXCEPT + : slot_list{etl::forward(slots)...} + , slot_list_end{slot_list + sizeof...(slots)} + { + static_assert((etl::are_all_same...>::value), "All slots must be slot_type"); + static_assert(sizeof...(slots) <= Size, "Number of slots exceeds capacity"); + } + + //************************************************************************* + ///\brief Connects a slot to the signal. + /// Ignores the slot if it has already been connected. + /// + ///\param slot: The slot to connect. + ///\return false if not all slots could be connected. + //************************************************************************* + bool connect(const slot_type& slot) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + { + if (!connected(slot)) + { + ETL_ASSERT_OR_RETURN_VALUE(!full(), ETL_ERROR(signal_full), false); + append_slot(slot); + } + + return true; + } + +#if ETL_HAS_INITIALIZER_LIST && ETL_USING_CPP17 + //************************************************************************* + ///\brief Connects slots to the signal. + /// Ignores the slots if it has already been connected. + /// + ///\param slots: std::initializer_list of slots to connect. + ///\return false if not all slots could be connected. + //************************************************************************* + bool connect(std::initializer_list slots) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + { + for (const slot_type& slot : slots) + { + if (!connected(slot)) + { + ETL_ASSERT_OR_RETURN_VALUE(!full(), ETL_ERROR(signal_full), false); + append_slot(slot); + } + } + + return true; + } +#endif + + //************************************************************************* + ///\brief Connects slots to the signal. + /// Ignores the slots if it has already been connected. + /// + ///\param slots: etl::span of slots to connect. + ///\return false if not all slots could be connected. + //************************************************************************* + bool connect(const span_type slots) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) + { + for (const slot_type& slot : slots) + { + if (!connected(slot)) + { + ETL_ASSERT_OR_RETURN_VALUE(!full(), ETL_ERROR(signal_full), false); + append_slot(slot); + } + } + + return true; + } + + //************************************************************************* + ///\brief Disconnects a slot from the signal. + /// + ///\param slot: To disconnect. + //************************************************************************* + void disconnect(const slot_type& slot) ETL_NOEXCEPT + { + const auto end_itr = end(); + const auto itr = etl::find(begin(), end_itr, slot); + + if (itr != end_itr) + { + // Shifts all elements after 'itr' one position to the left. + etl::copy(etl::next(itr), end_itr, itr); + slot_list_end = etl::prev(slot_list_end); + } + } + +#if ETL_HAS_INITIALIZER_LIST && ETL_USING_CPP17 + //************************************************************************* + ///\brief Disconnects multiple slots from the signal. + /// + ///\param slot: std::intializer_list of slots to disconnect. + //************************************************************************* + void disconnect(std::initializer_list slots) ETL_NOEXCEPT + { + for (const slot_type& slot : slots) + { + disconnect(slot); + } + } +#endif + + //************************************************************************* + ///\brief Disconnects multiple slots from the signal. + /// + ///\param slot: etl::span of slots to disconnect. + //************************************************************************* + void disconnect(const span_type slots) ETL_NOEXCEPT + { + for (const slot_type& slot : slots) + { + disconnect(slot); + } + } + + //************************************************************************* + ///\brief Disconnects all slots from the signal. + //************************************************************************* + void disconnect_all() ETL_NOEXCEPT + { + slot_list_end = begin(); + } + + //************************************************************************* + ///\brief Checks if a slot is connected to the signal. + /// + ///\param slot: To check. + ///\return true if the slot is connected. + //************************************************************************* + ETL_CONSTEXPR14 bool connected(const slot_type& slot) const ETL_NOEXCEPT + { + return etl::any_of(begin(), end(), [&slot](const slot_type& s) { return s == slot; }); + } + + //************************************************************************* + ///\return true if the signal has no slots connected. + //************************************************************************* + ETL_CONSTEXPR14 bool empty() const ETL_NOEXCEPT + { + return begin() == end(); + } + + //************************************************************************* + ///\return true if the signal has the maximum number of slots connected. + //************************************************************************* + ETL_CONSTEXPR14 bool full() const ETL_NOEXCEPT + { + return size() == max_size(); + } + + //************************************************************************* + ///\return Total number of slots that can be connected. + //************************************************************************* + ETL_CONSTEXPR14 size_type max_size() const ETL_NOEXCEPT + { + return Size; + } + + //************************************************************************* + ///\return Total slots currently connected. + //************************************************************************* + ETL_CONSTEXPR14 size_type size() const ETL_NOEXCEPT + { + return etl::distance(begin(), end()); + } + + //************************************************************************* + ///\return Total empty slots available. + //************************************************************************* + ETL_CONSTEXPR14 size_type available() const ETL_NOEXCEPT + { + return max_size() - size(); + } + + //************************************************************************* + ///\brief Invokes all the slots connected to the signal. + /// Checks if the slot is valid to call. + /// + ///\param args: Arguments to pass to the slots. + //************************************************************************* + template + void operator()(TArgs&&... args) const ETL_NOEXCEPT + { + for (const slot_type& slot : *this) + { + if (slot_is_valid(slot)) + { + slot(etl::forward(args)...); + } + } + } + + private: + + using iterator = slot_type*; + using const_iterator = const slot_type*; + + slot_type slot_list[Size]; + iterator slot_list_end; + + //************************************************************************* + /// Appends a slot to the slot list. + //************************************************************************* + void append_slot(const slot_type& slot) ETL_NOEXCEPT + { + (*slot_list_end) = slot; + slot_list_end = etl::next(slot_list_end); + } + + //************************************************************************* + /// For a delegate slot type. + //************************************************************************* + template + static + typename etl::enable_if_t::value, bool> + slot_is_valid(const TSlotType& s) ETL_NOEXCEPT + { + return s.is_valid(); + } + + //************************************************************************* + /// For a non-delegate slot type. + //************************************************************************* + template + static + typename etl::enable_if_t::value, bool> + slot_is_valid(const TSlotType&) ETL_NOEXCEPT + { + return true; + } + + //************************************************************************* + ///\return Iterator to the beginning of the connected slots. + //************************************************************************* + ETL_CONSTEXPR14 iterator begin() ETL_NOEXCEPT + { + return slot_list; + } + + //************************************************************************* + ///\return Const Iterator to the beginning of the connected slots. + //************************************************************************* + ETL_CONSTEXPR14 const_iterator begin() const ETL_NOEXCEPT + { + return slot_list; + } + + //************************************************************************* + ///\return Iterator to the end of the connected slots. + //************************************************************************* + ETL_CONSTEXPR14 iterator end() ETL_NOEXCEPT + { + return slot_list_end; + } + + //************************************************************************* + ///\return Const Iterator to the end of the connected slots. + //************************************************************************* + ETL_CONSTEXPR14 const_iterator end() const ETL_NOEXCEPT + { + return slot_list_end; + } + }; +} + +#endif +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 888fd12c..c45dbc7a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -285,6 +285,7 @@ add_executable(etl_tests test_scaled_rounding.cpp test_set.cpp test_shared_message.cpp + test_signal.cpp test_singleton.cpp test_singleton_base.cpp test_smallest.cpp diff --git a/test/syntax_check/CMakeLists.txt b/test/syntax_check/CMakeLists.txt index e664a3a2..31629a73 100644 --- a/test/syntax_check/CMakeLists.txt +++ b/test/syntax_check/CMakeLists.txt @@ -290,6 +290,7 @@ target_sources(tests PRIVATE scheduler.h.t.cpp set.h.t.cpp shared_message.h.t.cpp + signal.h.t.cpp singleton.h.t.cpp singleton_base.h.t.cpp smallest.h.t.cpp diff --git a/test/syntax_check/signal.h.t.cpp b/test/syntax_check/signal.h.t.cpp new file mode 100644 index 00000000..5207afb1 --- /dev/null +++ b/test/syntax_check/signal.h.t.cpp @@ -0,0 +1,29 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2025 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include diff --git a/test/test_signal.cpp b/test/test_signal.cpp new file mode 100644 index 00000000..3d1f0690 --- /dev/null +++ b/test/test_signal.cpp @@ -0,0 +1,641 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2014 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++/CheckMacros.h" +#include "unit_test_framework.h" + +#include +#include + +#include "etl/signal.h" + +namespace +{ + using callback_type = void(std::ostream&); + using signal_type = etl::signal; + using slot_type = signal_type::slot_type; + using span_type = signal_type::span_type; + + //************************************************************************* + ///\brief Generic output free function + /// + ///\param out: output stream + ///\param str: input string + //************************************************************************* + void output(std::ostream& out, const std::string& str) + { + out << str; + } + + //************************************************************************* + ///\brief Free function that outputs "free" + /// + ///\param out: output + //************************************************************************* + void output_free(std::ostream& out) + { + output(out, "free"); + } + + //************************************************************************* + ///\brief Free function that outputs "free" + /// + ///\param out: output + //************************************************************************* + void output_extra(std::ostream& out) + { + output(out, "extra"); + } + + //************************************************************************* + ///\brief Lambda that outputs "lambda" + /// + ///\param out: output + //************************************************************************* + auto output_lambda = [](std::ostream& out) { output(out, "lambda"); }; + + class example_class + { + public: + //************************************************************************* + ///\brief Method that outputs "static" + /// + ///\param out: output + //************************************************************************* + static void static_method(std::ostream& out) + { + output(out, "static"); + } + + //************************************************************************* + ///\brief Method that outputs "method" + /// + ///\param out: output + //************************************************************************* + void method(std::ostream& out) + { + output(out, "method"); + } + + //************************************************************************* + ///\brief Functor method that outputs "functor" + /// + ///\param out: output + //************************************************************************* + void operator()(std::ostream& out) + { + output(out, "functor"); + } + }; + + example_class example; + + //************************************************************************* + ///\brief Makes an empty slot + /// + ///\return constexpr slot_type + //************************************************************************* + ETL_CONSTEXPR14 slot_type make_empty_slot() + { + return slot_type(); + } + + //************************************************************************* + ///\brief Makes the free function slot + /// + ///\return constexpr slot_type + //************************************************************************* + ETL_CONSTEXPR14 slot_type make_free_slot() + { + return slot_type::create(); + } + + //************************************************************************* + ///\brief Makes the lambda slot + /// + ///\return constexpr slot_type + //************************************************************************* + ETL_CONSTEXPR14 slot_type make_lambda_slot() + { + return slot_type{output_lambda}; + } + + //************************************************************************* + ///\brief Makes the static method slot + /// + ///\return constexpr slot_type + //************************************************************************* + ETL_CONSTEXPR14 slot_type make_static_slot() + { + return slot_type::create<&example_class::static_method>(); + } + + //************************************************************************* + ///\brief Makes the class method slot + /// + ///\return constexpr slot_type + //************************************************************************* + ETL_CONSTEXPR14 slot_type make_instance_slot() + { + return slot_type::create(); + } + + //************************************************************************* + ///\brief Makes the functor slot + /// + ///\return constexpr slot_type + //************************************************************************* + ETL_CONSTEXPR14 slot_type make_functor_slot() + { + return slot_type::create(); + } + + //************************************************************************* + ///\brief Makes the free function slot + /// + ///\return constexpr slot_type + //************************************************************************* + ETL_CONSTEXPR14 slot_type make_extra_slot() + { + return slot_type::create(); + } + + //************************************************************************* + ///\brief Makes the incorrect slot type + /// Uncomment for test_construct_with_incorrect_slot_type + ///\return An incorrect slot + //************************************************************************* + //ETL_CONSTEXPR14 etl::delegate make_incorrect_slot() + //{ + // return etl::delegate(); + //} + + //*************************************************************************** + SUITE(signal_test) + { + //*************************************************************************** + TEST(test_default_construct) + { + const signal_type test_object; + CHECK_EQUAL(0U, test_object.size()); + CHECK_EQUAL(5U, test_object.max_size()); + CHECK_TRUE(test_object.empty()); + CHECK_FALSE(test_object.full()); + } + +#if ETL_USING_CPP14 + //*************************************************************************** + TEST(test_constexpr_construct) + { + static constexpr signal_type signal + { + make_free_slot(), + make_lambda_slot(), + make_static_slot(), + make_instance_slot(), + make_functor_slot() + }; + + CHECK_EQUAL(5U, signal.size()); + CHECK_EQUAL(0, signal.available()); + CHECK_EQUAL(5U, signal.max_size()); + CHECK_FALSE(signal.empty()); + CHECK_TRUE(signal.full()); + } +#endif + + //*************************************************************************** + TEST(test_construct_from_slot_list) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + const signal_type signal{ free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + CHECK_EQUAL(5, signal.size()); + CHECK_EQUAL(0, signal.available()); + CHECK_EQUAL(5, signal.max_size()); + CHECK_FALSE(signal.empty()); + CHECK_TRUE(signal.full()); + } + + //*************************************************************************** + TEST(test_construct_from_too_many_slots) + { + // Uncomment to trigger static_assert "Number of slots exceeds capacity" + //const auto free_slot = make_free_slot(); + //const auto lambda_slot = make_lambda_slot(); + //const auto static_slot = make_static_slot(); + //const auto instance_slot = make_instance_slot(); + //const auto functor_slot = make_functor_slot(); + //const auto extra_slot = make_extra_slot(); + + //signal_type signal{ free_slot, lambda_slot, static_slot, instance_slot, functor_slot, extra_slot }; + } + +#if ETL_USING_CPP14 + //*************************************************************************** + TEST(test_constexpr_construct_from_slot_list) + { + static constexpr auto free_slot = make_free_slot(); + static constexpr auto lambda_slot = make_lambda_slot(); + static constexpr auto static_slot = make_static_slot(); + static constexpr auto instance_slot = make_instance_slot(); + static constexpr auto functor_slot = make_functor_slot(); + + static constexpr signal_type signal{ free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + CHECK_EQUAL(5, signal.size()); + CHECK_EQUAL(0, signal.available()); + CHECK_EQUAL(5, signal.max_size()); + CHECK_FALSE(signal.empty()); + CHECK_TRUE(signal.full()); + } +#endif + + //*************************************************************************** + TEST(test_construct_with_incorrect_slot_type) + { + // Uncomment to trigger static_assert "All slots must be slot_type" + //const auto free_slot = make_free_slot(); + //const auto lambda_slot = make_lambda_slot(); + //const auto incorrect_slot = make_incorrect_slot(); // Incorrect type + //const auto instance_slot = make_instance_slot(); + //const auto functor_slot = make_functor_slot(); + + //signal_type signal{ free_slot, lambda_slot, incorrect_slot, instance_slot, functor_slot }; + } + + //*************************************************************************** + TEST(test_connect) + { + signal_type signal; + + const auto free_slot = make_free_slot(); + CHECK_TRUE(signal.connect(free_slot)); + CHECK_EQUAL(1U, signal.size()); + CHECK_EQUAL(4U, signal.available()); + signal.connect(free_slot); // Try to connect it again - nothing should change. + CHECK_EQUAL(1U, signal.size()); + CHECK_EQUAL(4U, signal.available()); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + CHECK_TRUE(signal.connected(free_slot)); + + const auto lambda_slot = make_lambda_slot(); + CHECK_TRUE(signal.connect(lambda_slot)); + CHECK_EQUAL(2U, signal.size()); + CHECK_EQUAL(3U, signal.available()); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + CHECK_TRUE(signal.connected(lambda_slot)); + + const auto static_slot = make_static_slot(); + CHECK_TRUE(signal.connect(static_slot)); + CHECK_EQUAL(3U, signal.size()); + CHECK_EQUAL(2U, signal.available()); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + CHECK_TRUE(signal.connected(static_slot)); + + const auto instance_slot = make_instance_slot(); + CHECK_TRUE(signal.connect(instance_slot)); + CHECK_EQUAL(4U, signal.size()); + CHECK_EQUAL(1U, signal.available()); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + CHECK_TRUE(signal.connected(instance_slot)); + + const auto functor_slot = make_functor_slot(); + CHECK_TRUE(signal.connect(functor_slot)); + CHECK_EQUAL(5U, signal.size()); + CHECK_EQUAL(0U, signal.available()); + CHECK_FALSE(signal.empty()); + CHECK_TRUE(signal.full()); + CHECK_TRUE(signal.connected(functor_slot)); + } + +#if ETL_USING_CPP17 + //*************************************************************************** + TEST(test_connect_from_initializer_list) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + signal_type signal; + CHECK_TRUE(signal.connect({ free_slot, lambda_slot, static_slot, instance_slot, functor_slot })); + + CHECK_EQUAL(5U, signal.size()); + CHECK_TRUE(signal.connected(free_slot)); + CHECK_TRUE(signal.connected(lambda_slot)); + CHECK_TRUE(signal.connected(static_slot)); + CHECK_TRUE(signal.connected(instance_slot)); + CHECK_TRUE(signal.connected(functor_slot)); + CHECK_EQUAL(0U, signal.available()); + CHECK_FALSE(signal.empty()); + CHECK_TRUE(signal.full()); + } +#endif + +#if ETL_USING_CPP17 + //*************************************************************************** + TEST(test_connect_from_initializer_list_too_many_slots) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + const auto extra_slot = make_extra_slot(); + + signal_type signal; + CHECK_THROW(signal.connect({ free_slot, lambda_slot, static_slot, instance_slot, functor_slot, extra_slot }), etl::signal_full); + } +#endif + + //*************************************************************************** + TEST(test_connect_from_span) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + const slot_type slot_list[] = { free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + signal_type signal; + CHECK_TRUE(signal.connect(slot_list)); + } + + //*************************************************************************** + TEST(test_connect_from_span_too_many_slots) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + const auto extra_slot = make_extra_slot(); + + const slot_type slot_list[] = { free_slot, lambda_slot, static_slot, instance_slot, functor_slot, extra_slot }; + + signal_type signal; + CHECK_THROW(signal.connect(slot_list), etl::signal_full); + } + + //*************************************************************************** + TEST(test_disconnect) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + const slot_type slot_list[] = { free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + signal_type signal; + CHECK_TRUE(signal.connect(slot_list)); + + signal.disconnect(free_slot); + CHECK_EQUAL(4U, signal.size()); + CHECK_EQUAL(1U, signal.available()); + CHECK_FALSE(signal.connected(free_slot)); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + + // Try to remove it again - nothing should change. + signal.disconnect(free_slot); + CHECK_EQUAL(4U, signal.size()); + CHECK_EQUAL(1U, signal.available()); + CHECK_FALSE(signal.connected(free_slot)); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + + signal.disconnect(lambda_slot); + CHECK_EQUAL(3U, signal.size()); + CHECK_EQUAL(2U, signal.available()); + CHECK_FALSE(signal.connected(lambda_slot)); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + + signal.disconnect(static_slot); + CHECK_EQUAL(2U, signal.size()); + CHECK_EQUAL(3U, signal.available()); + CHECK_FALSE(signal.connected(static_slot)); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + + signal.disconnect(instance_slot); + CHECK_EQUAL(1U, signal.size()); + CHECK_EQUAL(4U, signal.available()); + CHECK_FALSE(signal.connected(instance_slot)); + CHECK_FALSE(signal.empty()); + CHECK_FALSE(signal.full()); + + signal.disconnect(functor_slot); + CHECK_EQUAL(0U, signal.size()); + CHECK_EQUAL(5U, signal.available()); + CHECK_FALSE(signal.connected(functor_slot)); + CHECK_TRUE(signal.empty()); + CHECK_FALSE(signal.full()); + } + } + +#if ETL_USING_CPP17 + //*************************************************************************** + TEST(test_disconnect_from_initializer_list) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + signal_type signal; + CHECK_TRUE(signal.connect({ free_slot, lambda_slot, static_slot, instance_slot, functor_slot })); + + signal.disconnect({ lambda_slot, instance_slot }); + CHECK_EQUAL(3U, signal.size()); + CHECK_FALSE(signal.empty()); + CHECK_TRUE(signal.connected(free_slot)); + CHECK_FALSE(signal.connected(lambda_slot)); + CHECK_TRUE(signal.connected(static_slot)); + CHECK_FALSE(signal.connected(instance_slot)); + CHECK_TRUE(signal.connected(functor_slot)); + } +#endif + + //*************************************************************************** + TEST(test_disconnect_from_span_list) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + signal_type signal; + CHECK_TRUE(signal.connect(free_slot)); + CHECK_TRUE(signal.connect(lambda_slot)); + CHECK_TRUE(signal.connect(static_slot)); + CHECK_TRUE(signal.connect(instance_slot)); + CHECK_TRUE(signal.connect(functor_slot)); + + const slot_type slot_list[] = { lambda_slot, instance_slot }; + + signal.disconnect(span_type(slot_list)); + CHECK_EQUAL(3U, signal.size()); + CHECK_FALSE(signal.empty()); + CHECK_TRUE(signal.connected(free_slot)); + CHECK_FALSE(signal.connected(lambda_slot)); + CHECK_TRUE(signal.connected(static_slot)); + CHECK_FALSE(signal.connected(instance_slot)); + CHECK_TRUE(signal.connected(functor_slot)); + } + + //*************************************************************************** + TEST(test_disconnect_all) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + const slot_type slot_list[] = { free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + signal_type signal; + CHECK_TRUE(signal.connect(slot_list)); + + signal.disconnect_all(); + CHECK_EQUAL(0U, signal.size()); + CHECK_TRUE(signal.empty()); + CHECK_FALSE(signal.connected(free_slot)); + CHECK_FALSE(signal.connected(lambda_slot)); + CHECK_FALSE(signal.connected(static_slot)); + CHECK_FALSE(signal.connected(instance_slot)); + CHECK_FALSE(signal.connected(functor_slot)); + } + + //*************************************************************************** + TEST(test_call_empty) + { + std::stringstream ss; + signal_type signal; + signal(ss); + CHECK_EQUAL(std::string{""}, ss.str()); + } + + //*************************************************************************** + TEST(test_call) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_lambda_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_instance_slot(); + const auto functor_slot = make_functor_slot(); + + signal_type signal{ free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + std::stringstream ss; + signal(ss); + + // Expect all signals got called + const std::string expected_string{"freelambdastaticmethodfunctor"}; + CHECK_EQUAL(expected_string, ss.str()); + } + +#if ETL_USING_CPP14 + //*************************************************************************** + TEST(test_constexpr_call) + { + static constexpr auto free_slot = make_free_slot(); + static constexpr auto lambda_slot = make_lambda_slot(); + static constexpr auto static_slot = make_static_slot(); + static constexpr auto instance_slot = make_instance_slot(); + static constexpr auto functor_slot = make_functor_slot(); + + static constexpr signal_type signal{ free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + std::stringstream ss; + signal(ss); + + // Expect all slots got called + const std::string expected_string{"freelambdastaticmethodfunctor"}; + CHECK_EQUAL(expected_string, ss.str()); + } +#endif + + //*************************************************************************** + TEST(test_call_empty_slots) + { + const auto free_slot = make_free_slot(); + const auto lambda_slot = make_empty_slot(); + const auto static_slot = make_static_slot(); + const auto instance_slot = make_empty_slot(); + const auto functor_slot = make_functor_slot(); + + signal_type signal{ free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + std::stringstream ss; + signal(ss); + + // Expect only valid slots got called + const std::string expected_string{"freestaticfunctor"}; + CHECK_EQUAL(expected_string, ss.str()); + } + +#if ETL_USING_CPP14 + //*************************************************************************** + TEST(test_call_constexpr_empty_slots) + { + static constexpr auto free_slot = make_free_slot(); + static constexpr auto lambda_slot = make_empty_slot(); + static constexpr auto static_slot = make_static_slot(); + static constexpr auto instance_slot = make_empty_slot(); + static constexpr auto functor_slot = make_functor_slot(); + + static constexpr signal_type signal{ free_slot, lambda_slot, static_slot, instance_slot, functor_slot }; + + std::stringstream ss; + signal(ss); + + // Expect only valid slots got called + const std::string expected_string{"freestaticfunctor"}; + CHECK_EQUAL(expected_string, ss.str()); + } +#endif +} diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 142d0136..76754ebc 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3651,6 +3651,7 @@ + @@ -8927,6 +8928,26 @@ true true + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true true @@ -11265,6 +11286,7 @@ true true + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 33a5afd7..7c816a12 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -1422,12 +1422,6 @@ Tests\Error Handler\Assert Function - - UnitTest++\Header Files - - - UnitTest++\Header Files - ETL\Utilities @@ -1506,9 +1500,6 @@ ETL\Maths\CRC - - UnitTest++\Header Files - ETL\Containers @@ -1524,6 +1515,18 @@ ETL\Utilities + + ETL\Patterns + + + ETL\Utilities + + + ETL\Utilities + + + ETL\Maths\CRC + @@ -3713,6 +3716,21 @@ Tests\Syntax Checks\Source + + Tests + + + Tests\Syntax Checks\Source + + + Tests\Misc + + + Tests + + + Tests\Syntax Checks\Source + Tests\Misc