From ea397ec2ddc4b2e0f3e0aeb8b3244f1ae1ef748b Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Tue, 31 Mar 2026 11:23:49 +0100 Subject: [PATCH] Changed non-capturing lambda API to runtime function pointer API --- include/etl/private/delegate_cpp11.h | 232 +++++++++++++++++---------- test/test_delegate.cpp | 77 ++++----- 2 files changed, 186 insertions(+), 123 deletions(-) diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h index c7d41e25..51f0b271 100644 --- a/include/etl/private/delegate_cpp11.h +++ b/include/etl/private/delegate_cpp11.h @@ -118,6 +118,11 @@ namespace etl template class delegate final : public delegate_tag { + private: + + using object_ptr = void*; + using function_ptr = TReturn(*)(TArgs...); + public: using return_type = TReturn; @@ -141,7 +146,7 @@ namespace etl template ::value && !is_delegate::value, void>> ETL_CONSTEXPR14 delegate(TLambda& instance) ETL_NOEXCEPT { - assign((void*)(&instance), lambda_stub); + assign(object_ptr(&instance), lambda_stub); } //************************************************************************* @@ -150,26 +155,24 @@ namespace etl template ::value && !is_delegate::value, void>> ETL_CONSTEXPR14 delegate(const TLambda& instance) ETL_NOEXCEPT { - assign((void*)(&instance), const_lambda_stub); + assign(object_ptr(&instance), const_lambda_stub); } //************************************************************************* // Delete construction from rvalue reference lambda or functor. // Excludes non-capturing lambdas convertible to a function pointer. //************************************************************************* - template ::value && !etl::is_same, TLambda>::value && !etl::is_convertible::value, void>> + template ::value && + !etl::is_same, TLambda>::value && + !etl::is_convertible::value, void>> ETL_CONSTEXPR14 delegate(TLambda&& instance) = delete; //************************************************************************* - // Construct from non-capturing rvalue lambda convertible to function pointer. - // Converts to a function pointer to avoid storing a dangling pointer - // to a destroyed temporary. + // Construct from a function pointer. //************************************************************************* - template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> - delegate(TLambda&& instance) ETL_NOEXCEPT + delegate(function_ptr fp) ETL_NOEXCEPT { - TReturn(*fp)(TArgs...) = static_cast(instance); - assign(reinterpret_cast(fp), function_ptr_stub); + assign(fp, function_ptr_stub); } //************************************************************************* @@ -179,7 +182,7 @@ namespace etl ETL_NODISCARD static ETL_CONSTEXPR14 delegate create() ETL_NOEXCEPT { - return delegate(ETL_NULLPTR, function_stub); + return delegate(function_stub); } //************************************************************************* @@ -189,7 +192,7 @@ namespace etl ETL_NODISCARD static ETL_CONSTEXPR14 delegate create(TLambda& instance) ETL_NOEXCEPT { - return delegate((void*)(&instance), lambda_stub); + return delegate(object_ptr(&instance), lambda_stub); } //************************************************************************* @@ -199,20 +202,16 @@ namespace etl ETL_NODISCARD static ETL_CONSTEXPR14 delegate create(const TLambda& instance) ETL_NOEXCEPT { - return delegate((void*)(&instance), const_lambda_stub); + return delegate(object_ptr(&instance), const_lambda_stub); } //************************************************************************* - // Create from non-capturing rvalue lambda convertible to function pointer. - // Converts to a function pointer to avoid storing a dangling pointer - // to a destroyed temporary. + // Create from a function pointer. //************************************************************************* - template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> ETL_NODISCARD - static delegate create(TLambda&& instance) ETL_NOEXCEPT + static delegate create(function_ptr fp) ETL_NOEXCEPT { - TReturn(*fp)(TArgs...) = static_cast(instance); - return delegate(reinterpret_cast(fp), function_ptr_stub); + return delegate(fp, function_ptr_stub); } //************************************************************************* @@ -222,7 +221,7 @@ namespace etl ETL_NODISCARD static ETL_CONSTEXPR14 delegate create(T& instance) ETL_NOEXCEPT { - return delegate((void*)(&instance), method_stub); + return delegate(object_ptr(&instance), method_stub); } //************************************************************************* @@ -240,7 +239,7 @@ namespace etl ETL_NODISCARD static ETL_CONSTEXPR14 delegate create(const T& instance) ETL_NOEXCEPT { - return delegate((void*)(&instance), const_method_stub); + return delegate(object_ptr(&instance), const_method_stub); } //************************************************************************* @@ -310,7 +309,7 @@ namespace etl template ETL_CONSTEXPR14 void set() ETL_NOEXCEPT { - assign(ETL_NULLPTR, function_stub); + assign(function_stub); } //************************************************************************* @@ -319,7 +318,7 @@ namespace etl template ::value && !is_delegate::value, void>> ETL_CONSTEXPR14 void set(TLambda& instance) ETL_NOEXCEPT { - assign((void*)(&instance), lambda_stub); + assign(object_ptr(&instance), lambda_stub); } //************************************************************************* @@ -328,19 +327,15 @@ namespace etl template ::value && !is_delegate::value, void>> ETL_CONSTEXPR14 void set(const TLambda& instance) ETL_NOEXCEPT { - assign((void*)(&instance), const_lambda_stub); + assign(object_ptr(&instance), const_lambda_stub); } //************************************************************************* - // Set from non-capturing rvalue lambda convertible to function pointer. - // Converts to a function pointer to avoid storing a dangling pointer - // to a destroyed temporary. + // Set from a function pointer. //************************************************************************* - template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> - void set(TLambda&& instance) ETL_NOEXCEPT + void set(function_ptr fp) ETL_NOEXCEPT { - TReturn(*fp)(TArgs...) = static_cast(instance); - assign(reinterpret_cast(fp), function_ptr_stub); + assign(fp, function_ptr_stub); } //************************************************************************* @@ -349,7 +344,7 @@ namespace etl template ETL_CONSTEXPR14 void set(T& instance) ETL_NOEXCEPT { - assign((void*)(&instance), method_stub); + assign(object_ptr(&instance), method_stub); } //************************************************************************* @@ -358,7 +353,7 @@ namespace etl template ETL_CONSTEXPR14 void set(T& instance) ETL_NOEXCEPT { - assign((void*)(&instance), const_method_stub); + assign(object_ptr(&instance), const_method_stub); } //************************************************************************* @@ -367,7 +362,7 @@ namespace etl template ETL_CONSTEXPR14 void set() ETL_NOEXCEPT { - assign(ETL_NULLPTR, method_instance_stub); + assign(method_instance_stub); } //************************************************************************* @@ -377,7 +372,7 @@ namespace etl template ETL_CONSTEXPR14 void set() ETL_NOEXCEPT { - assign(ETL_NULLPTR, method_instance_stub); + assign(method_instance_stub); } //************************************************************************* @@ -386,7 +381,7 @@ namespace etl template ETL_CONSTEXPR14 void set() ETL_NOEXCEPT { - assign(ETL_NULLPTR, const_method_instance_stub); + assign(const_method_instance_stub); } //************************************************************************* @@ -396,7 +391,7 @@ namespace etl template ETL_CONSTEXPR14 void set() ETL_NOEXCEPT { - assign(ETL_NULLPTR, const_method_instance_stub); + assign(const_method_instance_stub); } //************************************************************************* @@ -419,7 +414,7 @@ namespace etl ETL_ASSERT(is_valid(), ETL_ERROR(delegate_uninitialised)); - return (*invocation.stub)(invocation.object, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } //************************************************************************* @@ -436,7 +431,7 @@ namespace etl if (is_valid()) { - (*invocation.stub)(invocation.object, etl::forward(args)...); + (*invocation.stub)(invocation, etl::forward(args)...); return true; } else @@ -461,7 +456,7 @@ namespace etl if (is_valid()) { - result = (*invocation.stub)(invocation.object, etl::forward(args)...); + result = (*invocation.stub)(invocation, etl::forward(args)...); } return result; @@ -479,7 +474,7 @@ namespace etl if (is_valid()) { - return (*invocation.stub)(invocation.object, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } else { @@ -499,7 +494,7 @@ namespace etl if (is_valid()) { - return (*invocation.stub)(invocation.object, etl::forward(args)...); + return (*invocation.stub)(invocation, etl::forward(args)...); } else { @@ -518,7 +513,7 @@ namespace etl template ::value && !is_delegate::value, void>> ETL_CONSTEXPR14 delegate& operator =(TLambda& instance) ETL_NOEXCEPT { - assign((void*)(&instance), lambda_stub); + assign(object_ptr(&instance), lambda_stub); return *this; } @@ -528,20 +523,16 @@ namespace etl template ::value && !is_delegate::value, void>> ETL_CONSTEXPR14 delegate& operator =(const TLambda& instance) ETL_NOEXCEPT { - assign((void*)(&instance), const_lambda_stub); + assign(object_ptr(&instance), const_lambda_stub); return *this; } //************************************************************************* - // Create from non-capturing rvalue lambda convertible to function pointer. - // Converts to a function pointer to avoid storing a dangling pointer - // to a destroyed temporary. + // Create from a function pointer. //************************************************************************* - template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> - delegate& operator =(TLambda&& instance) ETL_NOEXCEPT + delegate& operator =(function_ptr fp) ETL_NOEXCEPT { - TReturn(*fp)(TArgs...) = static_cast(instance); - assign(reinterpret_cast(fp), function_ptr_stub); + assign(fp, function_ptr_stub); return *this; } @@ -582,13 +573,11 @@ namespace etl private: - using stub_type = TReturn(*)(void* object, TArgs...); - //************************************************************************* // Callable compatibility: detects if C (or const C) is invocable with (TArgs...) and returns a type // convertible to TReturn. Works with generic lambdas and functors. template - struct is_invocable_with + struct is_invocable_with : etl::false_type {}; template @@ -614,11 +603,32 @@ namespace etl //************************************************************************* struct invocation_element { - invocation_element() = default; + using stub_type = TReturn(*)(const invocation_element&, TArgs...); //*********************************************************************** - ETL_CONSTEXPR14 invocation_element(void* object_, stub_type stub_) ETL_NOEXCEPT - : object(object_) + ETL_CONSTEXPR14 invocation_element() ETL_NOEXCEPT + : ptr(object_ptr(ETL_NULLPTR)) + , stub(ETL_NULLPTR) + { + } + + //*********************************************************************** + ETL_CONSTEXPR14 invocation_element(stub_type stub_) ETL_NOEXCEPT + : ptr() + , stub(stub_) + { + } + + //*********************************************************************** + ETL_CONSTEXPR14 invocation_element(object_ptr object_, stub_type stub_) ETL_NOEXCEPT + : ptr(object_) + , stub(stub_) + { + } + + //*********************************************************************** + ETL_CONSTEXPR14 invocation_element(function_ptr fp_, stub_type stub_) ETL_NOEXCEPT + : ptr(fp_) , stub(stub_) { } @@ -626,59 +636,109 @@ namespace etl //*********************************************************************** ETL_CONSTEXPR14 bool operator ==(const invocation_element& rhs) const ETL_NOEXCEPT { - return (rhs.stub == stub) && (rhs.object == object); + return (rhs.stub == stub) && + ((stub == function_ptr_stub) ? (rhs.ptr.fp == ptr.fp) + : (rhs.ptr.object == ptr.object)); } //*********************************************************************** ETL_CONSTEXPR14 bool operator !=(const invocation_element& rhs) const ETL_NOEXCEPT { - return (rhs.stub != stub) || (rhs.object != object); + return !operator==(rhs); } //*********************************************************************** ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT { - object = ETL_NULLPTR; - stub = ETL_NULLPTR; + stub = ETL_NULLPTR; } //*********************************************************************** - void* object = ETL_NULLPTR; - stub_type stub = ETL_NULLPTR; + union ptr_type + { + ETL_CONSTEXPR14 ptr_type() ETL_NOEXCEPT + : object(ETL_NULLPTR) + { + } + + ETL_CONSTEXPR14 ptr_type(object_ptr object_) ETL_NOEXCEPT + : object(object_) + { + } + + ETL_CONSTEXPR14 ptr_type(function_ptr fp_) ETL_NOEXCEPT + : fp(fp_) + { + } + + object_ptr object; + function_ptr fp; + }; + + ptr_type ptr; + stub_type stub; }; + using stub_type = typename invocation_element::stub_type; + //************************************************************************* /// Constructs a delegate from an object and stub. //************************************************************************* - ETL_CONSTEXPR14 delegate(void* object, stub_type stub) ETL_NOEXCEPT + ETL_CONSTEXPR14 delegate(object_ptr object, stub_type stub) ETL_NOEXCEPT : invocation(object, stub) { } + //************************************************************************* + /// Constructs a delegate from an function and stub. + //************************************************************************* + ETL_CONSTEXPR14 delegate(function_ptr fp, stub_type stub) ETL_NOEXCEPT + : invocation(fp, stub) + { + } + //************************************************************************* /// Constructs a delegate from a stub. //************************************************************************* ETL_CONSTEXPR14 delegate(stub_type stub) ETL_NOEXCEPT - : invocation(ETL_NULLPTR, stub) + : invocation(stub) { } //************************************************************************* /// Assign from an object and stub. //************************************************************************* - ETL_CONSTEXPR14 void assign(void* object, stub_type stub) ETL_NOEXCEPT + ETL_CONSTEXPR14 void assign(object_ptr object, stub_type stub) ETL_NOEXCEPT { - invocation.object = object; + invocation.ptr.object = object; + invocation.stub = stub; + } + + //************************************************************************* + /// Assign from a function and stub. + //************************************************************************* + ETL_CONSTEXPR14 void assign(function_ptr fp, stub_type stub) ETL_NOEXCEPT + { + invocation.ptr.fp = fp; invocation.stub = stub; } + //************************************************************************* + /// Assign from a stub. + //************************************************************************* + ETL_CONSTEXPR14 void assign(stub_type stub) ETL_NOEXCEPT + { + invocation.ptr.object = ETL_NULLPTR; + invocation.stub = stub; + } + //************************************************************************* /// Stub call for a member function. Run time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn method_stub(void* object, TArgs... args) + static ETL_CONSTEXPR14 TReturn method_stub(const invocation_element& invocation, TArgs... args) { - T* p = static_cast(object); + T* p = static_cast(invocation.ptr.object); return (p->*Method)(etl::forward(args)...); } @@ -686,9 +746,9 @@ namespace etl /// Stub call for a const member function. Run time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn const_method_stub(void* object, TArgs... args) + static ETL_CONSTEXPR14 TReturn const_method_stub(const invocation_element& invocation, TArgs... args) { - T* const p = static_cast(object); + T* const p = static_cast(invocation.ptr.object); return (p->*Method)(etl::forward(args)...); } @@ -696,7 +756,7 @@ namespace etl /// Stub call for a member function. Compile time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn method_instance_stub(void*, TArgs... args) + static ETL_CONSTEXPR14 TReturn method_instance_stub(const invocation_element&, TArgs... args) { return (Instance.*Method)(etl::forward(args)...); } @@ -705,7 +765,7 @@ namespace etl /// Stub call for a const member function. Compile time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn const_method_instance_stub(void*, TArgs... args) + static ETL_CONSTEXPR14 TReturn const_method_instance_stub(const invocation_element&, TArgs... args) { return (Instance.*Method)(etl::forward(args)...); } @@ -715,7 +775,7 @@ namespace etl /// Stub call for a function operator. Compile time instance. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn operator_instance_stub(void*, TArgs... args) + static ETL_CONSTEXPR14 TReturn operator_instance_stub(const invocation_element&, TArgs... args) { return Instance.operator()(etl::forward(args)...); } @@ -725,30 +785,28 @@ namespace etl /// Stub call for a free function. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn function_stub(void*, TArgs... args) + static ETL_CONSTEXPR14 TReturn function_stub(const invocation_element&, TArgs... args) { return (Method)(etl::forward(args)...); } //************************************************************************* - /// Stub call for a runtime function pointer stored in the object field. + /// Stub call for a runtime function pointer stored in the invocation_element. //************************************************************************* - static TReturn function_ptr_stub(void* object, TArgs... args) + static TReturn function_ptr_stub(const invocation_element& invocation, TArgs... args) { - ETL_STATIC_ASSERT(sizeof(void*) >= sizeof(TReturn(*)(TArgs...)), "etl::delegate: function pointer too large to store in object field"); - TReturn(*fp)(TArgs...) = reinterpret_cast(object); - return fp(etl::forward(args)...); + return invocation.ptr.fp(etl::forward(args)...); } //************************************************************************* /// Stub call for a lambda or functor function. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn lambda_stub(void* object, TArgs... arg) + static ETL_CONSTEXPR14 TReturn lambda_stub(const invocation_element& invocation, TArgs... arg) { ETL_STATIC_ASSERT(is_compatible_callable::value, "etl::delegate: bound lambda/functor is not compatible with the delegate signature"); - - TLambda* p = static_cast(object); + + TLambda* p = static_cast(invocation.ptr.object); return (p->operator())(etl::forward(arg)...); } @@ -756,11 +814,11 @@ namespace etl /// Stub call for a const lambda or functor function. //************************************************************************* template - static ETL_CONSTEXPR14 TReturn const_lambda_stub(void* object, TArgs... arg) + static ETL_CONSTEXPR14 TReturn const_lambda_stub(const invocation_element& invocation, TArgs... arg) { ETL_STATIC_ASSERT(is_compatible_callable::value, "etl::delegate: bound lambda/functor is not compatible with the delegate signature"); - - const TLambda* p = static_cast(object); + + const TLambda* p = static_cast(invocation.ptr.object); return (p->operator())(etl::forward(arg)...); } diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index 3f6d8f5a..5ec1af4f 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -274,7 +274,7 @@ namespace }; //******************************************* - int times_2(int a) + int times_2(int a) { return a * 2; } @@ -315,7 +315,7 @@ namespace // Check the return type. CHECK_TRUE((std::is_same::value)); - + // Check the argument types. CHECK_TRUE((std::is_same>::value)); } @@ -636,7 +636,7 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_construct_from_rvalue_non_capturing_lambda) { - etl::delegate d([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j; }); + etl::delegate d(+[](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j; }); int result = d(VALUE1, VALUE2); @@ -650,7 +650,7 @@ namespace { etl::delegate d; - d = [](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 2; }; + d = +[](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 2; }; int result = d(VALUE1, VALUE2); @@ -662,7 +662,7 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_create_from_rvalue_non_capturing_lambda) { - auto d = etl::delegate::create([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 5; }); + auto d = etl::delegate::create(+[](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 5; }); int result = d(VALUE1, VALUE2); @@ -1221,20 +1221,20 @@ namespace } //************************************************************************* -//#if ETL_USING_CPP17 -// TEST_FIXTURE(SetupFixture, test_make_delegate_member_static) -// { -// auto d = etl::make_delegate(); -// -// Data data; -// data.d = VALUE1; -// -// d(data, VALUE2); -// -// CHECK(function_called == FunctionCalled::Member_Static_Called); -// CHECK(parameter_correct); -// } -//#endif +#if ETL_USING_CPP17 + TEST_FIXTURE(SetupFixture, test_make_delegate_member_static) + { + auto d = etl::make_delegate(); + + Data data; + data.d = VALUE1; + + d(data, VALUE2); + + CHECK(function_called == FunctionCalled::Member_Static_Called); + CHECK(parameter_correct); + } +#endif //************************************************************************* #if ETL_USING_CPP14 @@ -1253,20 +1253,20 @@ namespace #endif //************************************************************************* -//#if ETL_USING_CPP17 -// TEST_FIXTURE(SetupFixture, test_make_delegate_member_static_constexpr) -// { -// constexpr auto d = etl::make_delegate(); -// -// Data data; -// data.d = VALUE1; -// -// d(data, VALUE2); -// -// CHECK(function_called == FunctionCalled::Member_Static_Called); -// CHECK(parameter_correct); -// } -//#endif +#if ETL_USING_CPP17 + TEST_FIXTURE(SetupFixture, test_make_delegate_member_static_constexpr) + { + constexpr auto d = etl::make_delegate(); + + Data data; + data.d = VALUE1; + + d(data, VALUE2); + + CHECK(function_called == FunctionCalled::Member_Static_Called); + CHECK(parameter_correct); + } +#endif #if !(defined(ETL_COMPILER_GCC) && (__GNUC__ <= 5)) //************************************************************************* @@ -1677,7 +1677,7 @@ namespace TEST_FIXTURE(SetupFixture, test_set_free_int) { etl::delegate d; - + d.set(); d(VALUE1, VALUE2); @@ -1690,7 +1690,7 @@ namespace TEST_FIXTURE(SetupFixture, test_set_lambda_int) { etl::delegate d; - + d.set([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); }); d(VALUE1, VALUE2); @@ -1704,7 +1704,7 @@ namespace { Object object; etl::delegate d; - + d.set(object); Data data; @@ -1851,8 +1851,11 @@ namespace auto d1 = etl::delegate::create(object); auto d2 = d1; + auto d3 = etl::delegate::create([](int, int) { }); + auto d4 = d3; CHECK(d1 == d2); + CHECK(d3 == d4); } //************************************************************************* @@ -1862,8 +1865,10 @@ namespace auto d1 = etl::delegate::create(object); auto d2 = etl::delegate::create(object); + auto d3 = etl::delegate::create([](int, int) { }); CHECK(d1 != d2); + CHECK(d1 != d3); } //*************************************************************************