From 363d2e8ab557fe7ec98410f2547f53f56f1b03e4 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 6 Sep 2025 21:50:58 +0200 Subject: [PATCH] Make call interfaces in etl::delegate and etl::closure conditionally noexcept (#1172) * Make all call interfaces in etl::delegate conditionally noexcept This covers the case when the invoked code can throw. For operator(), this was already implemented. Do similarly for the other call interfaces. * Make all call interfaces in etl::closure conditionally noexcept Similar to etl::delegate, the contained delegate might be throwing. Apply the same solution as in etl::delegate to make the call interfaces conditionally noexcept. --- include/etl/closure.h | 14 +++++------ include/etl/private/delegate_cpp11.h | 24 +++++++++---------- test/test_closure.cpp | 27 +++++++++++++++++++++ test/test_delegate.cpp | 35 +++++++++++++++++++++++++++- 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/include/etl/closure.h b/include/etl/closure.h index 972a354e..bc4c137a 100644 --- a/include/etl/closure.h +++ b/include/etl/closure.h @@ -71,7 +71,7 @@ namespace etl /// \param f The delegate to be invoked. /// \param args The arguments to bind to the delegate. //********************************************************************* - ETL_CONSTEXPR14 closure(const delegate_type& f, const TArgs... args) ETL_NOEXCEPT + ETL_CONSTEXPR14 closure(const delegate_type& f, const TArgs... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) : m_f(f) , m_args(args...) { @@ -81,7 +81,7 @@ namespace etl /// Invoke the stored delegate with the bound arguments. /// \return The result of the delegate invocation. //********************************************************************* - ETL_CONSTEXPR14 TReturn operator()() const ETL_NOEXCEPT + ETL_CONSTEXPR14 TReturn operator()() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { return execute(etl::index_sequence_for{}); } @@ -94,7 +94,7 @@ namespace etl /// \param arg The new value to bind. //********************************************************************* template - ETL_CONSTEXPR14 void bind(UArg arg) ETL_NOEXCEPT + ETL_CONSTEXPR14 void bind(UArg arg) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { static_assert(etl::is_convertible>::value, "Argument is not convertible"); static_assert(!etl::is_reference::value, "Cannot bind reference arguments"); @@ -108,7 +108,7 @@ namespace etl /// \param args The new values to bind. ///********************************************************************* template - ETL_CONSTEXPR14 void bind(UArgs&&... args) ETL_NOEXCEPT + ETL_CONSTEXPR14 void bind(UArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { static_assert(sizeof...(UArgs) == sizeof...(TArgs), "Argument count mismatch"); bind_impl(etl::make_index_sequence{}, etl::forward(args)...); @@ -121,7 +121,7 @@ namespace etl /// \param args The new values to bind. ///********************************************************************* template - ETL_CONSTEXPR14 void bind_impl(etl::index_sequence, UArgs&&... args) ETL_NOEXCEPT + ETL_CONSTEXPR14 void bind_impl(etl::index_sequence, UArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { // Expand the pack and call bind(arg) for each argument int dummy[] = {0, (bind(etl::forward(args)), 0)...}; @@ -134,7 +134,7 @@ namespace etl /// \return The result of the delegate invocation. //********************************************************************* template - ETL_CONSTEXPR14 TReturn execute(etl::index_sequence) const ETL_NOEXCEPT + ETL_CONSTEXPR14 TReturn execute(etl::index_sequence) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { return m_f(etl::get(m_args)...); } @@ -147,7 +147,7 @@ namespace etl /// Base template for closure. //************************************************************************* template - class closure; + class closure; //************************************************************************* /// Closure for binding one argument to a delegate and invoking it later. diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index 346e022b..a922a3e3 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -386,7 +386,7 @@ namespace etl template ETL_CONSTEXPR14 typename etl::enable_if_t::value, bool> - call_if(TParams... args) const ETL_NOEXCEPT + call_if(TParams... args) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { if (is_valid()) { @@ -406,7 +406,7 @@ namespace etl template ETL_CONSTEXPR14 typename etl::enable_if_t::value, etl::optional> - call_if(TParams... args) const ETL_NOEXCEPT + call_if(TParams... args) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { etl::optional result; @@ -423,7 +423,7 @@ namespace etl /// Run time alternative. //************************************************************************* template - ETL_CONSTEXPR14 TReturn call_or(TAlternative alternative, TParams... args) const ETL_NOEXCEPT + ETL_CONSTEXPR14 TReturn call_or(TAlternative alternative, TParams... args) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { if (is_valid()) { @@ -440,7 +440,7 @@ namespace etl /// Compile time alternative. //************************************************************************* template - ETL_CONSTEXPR14 TReturn call_or(TParams... args) const ETL_NOEXCEPT + ETL_CONSTEXPR14 TReturn call_or(TParams... args) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { if (is_valid()) { @@ -583,7 +583,7 @@ namespace etl /// Stub call for a member function. Run time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn method_stub(void* object, TParams... params) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn method_stub(void* object, TParams... params) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { T* p = static_cast(object); return (p->*Method)(etl::forward(params)...); @@ -593,7 +593,7 @@ namespace etl /// Stub call for a const member function. Run time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn const_method_stub(void* object, TParams... params) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn const_method_stub(void* object, TParams... params) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { T* const p = static_cast(object); return (p->*Method)(etl::forward(params)...); @@ -603,7 +603,7 @@ namespace etl /// Stub call for a member function. Compile time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn method_instance_stub(void*, TParams... params) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn method_instance_stub(void*, TParams... params) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { return (Instance.*Method)(etl::forward(params)...); } @@ -612,7 +612,7 @@ namespace etl /// Stub call for a const member function. Compile time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn const_method_instance_stub(void*, TParams... params) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn const_method_instance_stub(void*, TParams... params) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { return (Instance.*Method)(etl::forward(params)...); } @@ -622,7 +622,7 @@ namespace etl /// Stub call for a function operator. Compile time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn operator_instance_stub(void*, TParams... params) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn operator_instance_stub(void*, TParams... params) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { return Instance.operator()(etl::forward(params)...); } @@ -632,7 +632,7 @@ namespace etl /// Stub call for a free function. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn function_stub(void*, TParams... params) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn function_stub(void*, TParams... params) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { return (Method)(etl::forward(params)...); } @@ -641,7 +641,7 @@ namespace etl /// Stub call for a lambda or functor function. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn lambda_stub(void* object, TParams... arg) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn lambda_stub(void* object, TParams... arg) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { TLambda* p = static_cast(object); return (p->operator())(etl::forward(arg)...); @@ -651,7 +651,7 @@ namespace etl /// Stub call for a const lambda or functor function. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn const_lambda_stub(void* object, TParams... arg) ETL_NOEXCEPT + static ETL_CONSTEXPR14 TReturn const_lambda_stub(void* object, TParams... arg) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS) { const TLambda* p = static_cast(object); return (p->operator())(etl::forward(arg)...); diff --git a/test/test_closure.cpp b/test/test_closure.cpp index 8687aab5..2a9f41a0 100644 --- a/test/test_closure.cpp +++ b/test/test_closure.cpp @@ -30,6 +30,8 @@ SOFTWARE. #include "etl/closure.h" +#include + namespace { SUITE(test_closure) @@ -39,6 +41,15 @@ namespace return a1 * 3; } + int f1_throwing(int) + { + throw std::runtime_error("throwing function"); + } + + void f1_void(int) + { + } + int f1_ref(int& a1) { return a1 * 3; @@ -65,6 +76,8 @@ namespace } etl::delegate df1 = etl::delegate::create<&f1>(); + etl::delegate df1_throwing = etl::delegate::create<&f1_throwing>(); + etl::delegate df1_void = etl::delegate::create<&f1_void>(); etl::delegate df2 = etl::delegate::create<&f2>(); etl::delegate df3 = etl::delegate::create<&f3>(); etl::delegate df4 = etl::delegate::create<&f4>(); @@ -99,6 +112,20 @@ namespace CHECK_EQUAL(16, c1_lambda()); } + //************************************************************************* + TEST(test_throwing) + { + etl::closure c1(df1_throwing, 4); + CHECK_THROW(c1(), std::runtime_error); + } + + //************************************************************************* + TEST(test_void) + { + etl::closure c1(df1_void, 4); + c1(); + } + //************************************************************************* TEST(test_2_args) { diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index a4fdf6c8..a6545b56 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -37,6 +37,7 @@ SOFTWARE. #include #include #include +#include namespace { @@ -159,6 +160,20 @@ namespace return i + j + 1; } + //***************************************************************************** + // The throwing function. + //***************************************************************************** + void throwing_void() + { + throw std::runtime_error("throwing function"); + } + + int throwing_normal(int, int) + { + throw std::runtime_error("throwing function with two parameters"); + } + + //***************************************************************************** // The test class with member functions. //***************************************************************************** @@ -358,6 +373,24 @@ namespace CHECK(function_called == FunctionCalled::Free_Void_Called); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_throwing) + { + { + auto d = etl::delegate::create(); + + CHECK_THROW(d(), std::runtime_error); + CHECK_THROW(d.call_if(), std::runtime_error); + } + + { + auto d = etl::delegate::create(); + + CHECK_THROW({d.call_or(alternative, VALUE1, VALUE2);}, std::runtime_error); + CHECK_THROW({d.call_or(VALUE1, VALUE2);}, std::runtime_error); + } + } + //************************************************************************* #if ETL_USING_CPP17 TEST_FIXTURE(SetupFixture, test_make_delegate_free_void) @@ -1426,7 +1459,7 @@ namespace //************************************************************************* #if ETL_USING_CPP17 - TEST_FIXTURE(SetupFixture, test_make_delegate_member_int_const_compile_tim1e_new_api) + TEST_FIXTURE(SetupFixture, test_make_delegate_member_int_const_compile_time_new_api) { auto d = etl::make_delegate();