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.
This commit is contained in:
Roland Reichwein 2025-09-06 21:50:58 +02:00 committed by GitHub
parent 92f07a66fc
commit 363d2e8ab5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 80 additions and 20 deletions

View File

@ -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<TArgs...>{});
}
@ -94,7 +94,7 @@ namespace etl
/// \param arg The new value to bind.
//*********************************************************************
template <size_t Index, typename UArg>
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<UArg, etl::type_list_type_at_index_t<argument_types, Index>>::value, "Argument is not convertible");
static_assert(!etl::is_reference<UArg>::value, "Cannot bind reference arguments");
@ -108,7 +108,7 @@ namespace etl
/// \param args The new values to bind.
///*********************************************************************
template <typename... UArgs>
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<sizeof...(TArgs)>{}, etl::forward<UArgs>(args)...);
@ -121,7 +121,7 @@ namespace etl
/// \param args The new values to bind.
///*********************************************************************
template <size_t... Indexes, typename... UArgs>
ETL_CONSTEXPR14 void bind_impl(etl::index_sequence<Indexes...>, UArgs&&... args) ETL_NOEXCEPT
ETL_CONSTEXPR14 void bind_impl(etl::index_sequence<Indexes...>, UArgs&&... args) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS)
{
// Expand the pack and call bind<Index>(arg) for each argument
int dummy[] = {0, (bind<Indexes>(etl::forward<UArgs>(args)), 0)...};
@ -134,7 +134,7 @@ namespace etl
/// \return The result of the delegate invocation.
//*********************************************************************
template<size_t... Indexes>
ETL_CONSTEXPR14 TReturn execute(etl::index_sequence<Indexes...>) const ETL_NOEXCEPT
ETL_CONSTEXPR14 TReturn execute(etl::index_sequence<Indexes...>) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS)
{
return m_f(etl::get<Indexes>(m_args)...);
}
@ -147,7 +147,7 @@ namespace etl
/// Base template for closure.
//*************************************************************************
template <typename>
class closure;
class closure;
//*************************************************************************
/// Closure for binding one argument to a delegate and invoking it later.

View File

@ -386,7 +386,7 @@ namespace etl
template <typename TRet = TReturn>
ETL_CONSTEXPR14
typename etl::enable_if_t<etl::is_same<TRet, void>::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 <typename TRet = TReturn>
ETL_CONSTEXPR14
typename etl::enable_if_t<!etl::is_same<TRet, void>::value, etl::optional<TReturn>>
call_if(TParams... args) const ETL_NOEXCEPT
call_if(TParams... args) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS)
{
etl::optional<TReturn> result;
@ -423,7 +423,7 @@ namespace etl
/// Run time alternative.
//*************************************************************************
template <typename TAlternative>
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 <TReturn(*Method)(TParams...)>
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 <typename T, TReturn(T::*Method)(TParams...)>
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<T*>(object);
return (p->*Method)(etl::forward<TParams>(params)...);
@ -593,7 +593,7 @@ namespace etl
/// Stub call for a const member function. Run time instance.
//*************************************************************************
template <typename T, TReturn(T::*Method)(TParams...) const>
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<T*>(object);
return (p->*Method)(etl::forward<TParams>(params)...);
@ -603,7 +603,7 @@ namespace etl
/// Stub call for a member function. Compile time instance.
//*************************************************************************
template <typename T, TReturn(T::*Method)(TParams...), T& Instance>
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<TParams>(params)...);
}
@ -612,7 +612,7 @@ namespace etl
/// Stub call for a const member function. Compile time instance.
//*************************************************************************
template <typename T, TReturn(T::*Method)(TParams...) const, const T& Instance>
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<TParams>(params)...);
}
@ -622,7 +622,7 @@ namespace etl
/// Stub call for a function operator. Compile time instance.
//*************************************************************************
template <typename T, T& Instance>
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<TParams>(params)...);
}
@ -632,7 +632,7 @@ namespace etl
/// Stub call for a free function.
//*************************************************************************
template <TReturn(*Method)(TParams...)>
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<TParams>(params)...);
}
@ -641,7 +641,7 @@ namespace etl
/// Stub call for a lambda or functor function.
//*************************************************************************
template <typename TLambda>
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<TLambda*>(object);
return (p->operator())(etl::forward<TParams>(arg)...);
@ -651,7 +651,7 @@ namespace etl
/// Stub call for a const lambda or functor function.
//*************************************************************************
template <typename TLambda>
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<const TLambda*>(object);
return (p->operator())(etl::forward<TParams>(arg)...);

View File

@ -30,6 +30,8 @@ SOFTWARE.
#include "etl/closure.h"
#include <stdexcept>
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<int(int)> df1 = etl::delegate<int(int)>::create<&f1>();
etl::delegate<int(int)> df1_throwing = etl::delegate<int(int)>::create<&f1_throwing>();
etl::delegate<void(int)> df1_void = etl::delegate<void(int)>::create<&f1_void>();
etl::delegate<int(int, int)> df2 = etl::delegate<int(int, int)>::create<&f2>();
etl::delegate<int(int, int, int)> df3 = etl::delegate<int(int, int, int)>::create<&f3>();
etl::delegate<int(int, int, int, int)> df4 = etl::delegate<int(int, int, int, int)>::create<&f4>();
@ -99,6 +112,20 @@ namespace
CHECK_EQUAL(16, c1_lambda());
}
//*************************************************************************
TEST(test_throwing)
{
etl::closure<int(int)> c1(df1_throwing, 4);
CHECK_THROW(c1(), std::runtime_error);
}
//*************************************************************************
TEST(test_void)
{
etl::closure<void(int)> c1(df1_void, 4);
c1();
}
//*************************************************************************
TEST(test_2_args)
{

View File

@ -37,6 +37,7 @@ SOFTWARE.
#include <functional>
#include <algorithm>
#include <type_traits>
#include <stdexcept>
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<void(void)>::create<throwing_void>();
CHECK_THROW(d(), std::runtime_error);
CHECK_THROW(d.call_if(), std::runtime_error);
}
{
auto d = etl::delegate<int(int, int)>::create<throwing_normal>();
CHECK_THROW({d.call_or(alternative, VALUE1, VALUE2);}, std::runtime_error);
CHECK_THROW({d.call_or<alternative>(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<Object, &Object::member_int_const, const_object_static>();