1- From bfbb7259e128b6fac0ac3736a8f8297533b332fd Mon Sep 17 00:00:00 2001
1+ From 33f9eca9697ebdfd4b2d751f229aada00f61cd2a Mon Sep 17 00:00:00 2001
22From: Benedek Kupper <kupper.benedek@gmail.com>
33Date: Mon, 9 Feb 2026 22:35:13 +0100
44Subject: [PATCH] delegate: allow constructing from non-capturing lambdas
55
66Signed-off-by: Benedek Kupper <kupper.benedek@gmail.com>
77---
8- include/etl/private/delegate_cpp11.h | 63 +++++++++++++++++++++++++++-
9- test/test_delegate.cpp | 47 +++++++++++++++++++--
10- 2 files changed, 105 insertions(+), 5 deletions(- )
8+ include/etl/private/delegate_cpp11.h | 46 ++++++++++++++++++++++++
9+ test/test_delegate.cpp | 52 ++++++++++++++++++++++++++++
10+ 2 files changed, 98 insertions(+)
1111
1212diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h
13- index 288c3a5..c7d41e2 100644
13+ index 0770e0f0..ee51d5ce 100644
1414--- a/include/etl/private/delegate_cpp11.h
1515+++ b/include/etl/private/delegate_cpp11.h
16- @@ -155,10 +155,23 @@ namespace etl
17-
18- //*************************************************************************
19- // Delete construction from rvalue reference lambda or functor.
20- + // Excludes non-capturing lambdas convertible to a function pointer.
21- //*************************************************************************
22- - template <typename TLambda, typename = etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_same<etl::delegate<TReturn(TArgs...)>, TLambda>::value, void>>
23- + template <typename TLambda, typename = etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_same<etl::delegate<TReturn(TArgs...)>, TLambda>::value && !etl::is_convertible<TLambda, TReturn(*)(TArgs...)>::value, void>>
16+ @@ -175,6 +175,17 @@ namespace etl
17+ void>>
2418 ETL_CONSTEXPR14 delegate(TLambda&& instance) = delete;
2519
2620+ //*************************************************************************
27- + // Construct from non-capturing rvalue lambda convertible to function pointer.
28- + // Converts to a function pointer to avoid storing a dangling pointer
29- + // to a destroyed temporary.
21+ + /// Construct from a non-capturing rvalue lambda convertible to a function
22+ + /// pointer. Converts to a function pointer to avoid storing a dangling
23+ + /// pointer to a destroyed temporary.
3024+ //*************************************************************************
31- + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, TReturn(*)(TArgs...) >::value, int> = 0>
25+ + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, function_ptr >::value, int> = 0>
3226+ delegate(TLambda&& instance) ETL_NOEXCEPT
27+ + : invocation(static_cast<function_ptr>(instance), function_ptr_stub)
3328+ {
34- + TReturn(*fp)(TArgs...) = static_cast<TReturn(*)(TArgs...)>(instance);
35- + assign(reinterpret_cast<void*>(fp), function_ptr_stub);
3629+ }
3730+
3831 //*************************************************************************
39- /// Create from function (Compile time) .
32+ /// Construct from a function pointer .
4033 //*************************************************************************
41- @@ -189 ,6 +202,19 @@ namespace etl
42- return delegate((void*)(&instance), const_lambda_stub<TLambda> );
34+ @@ -222 ,6 +233,18 @@ namespace etl
35+ return delegate(fp, function_ptr_stub );
4336 }
4437
4538+ //*************************************************************************
4639+ // Create from non-capturing rvalue lambda convertible to function pointer.
4740+ // Converts to a function pointer to avoid storing a dangling pointer
4841+ // to a destroyed temporary.
4942+ //*************************************************************************
50- + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, TReturn(*)(TArgs...) >::value, int> = 0>
43+ + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, function_ptr >::value, int> = 0>
5144+ ETL_NODISCARD
5245+ static delegate create(TLambda&& instance) ETL_NOEXCEPT
5346+ {
54- + TReturn(*fp)(TArgs...) = static_cast<TReturn(*)(TArgs...)>(instance);
55- + return delegate(reinterpret_cast<void*>(fp), function_ptr_stub);
47+ + return delegate(static_cast<function_ptr>(instance), function_ptr_stub);
5648+ }
5749+
5850 //*************************************************************************
5951 /// Create from instance method (Run time).
6052 //*************************************************************************
61- @@ -305 ,6 +331,18 @@ namespace etl
62- assign((void*)(&instance), const_lambda_stub<TLambda> );
53+ @@ -346 ,6 +369,17 @@ namespace etl
54+ assign(fp, function_ptr_stub );
6355 }
6456
6557+ //*************************************************************************
6658+ // Set from non-capturing rvalue lambda convertible to function pointer.
6759+ // Converts to a function pointer to avoid storing a dangling pointer
6860+ // to a destroyed temporary.
6961+ //*************************************************************************
70- + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, TReturn(*)(TArgs...) >::value, int> = 0>
62+ + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, function_ptr >::value, int> = 0>
7163+ void set(TLambda&& instance) ETL_NOEXCEPT
7264+ {
73- + TReturn(*fp)(TArgs...) = static_cast<TReturn(*)(TArgs...)>(instance);
74- + assign(reinterpret_cast<void*>(fp), function_ptr_stub);
65+ + assign(static_cast<function_ptr>(instance), function_ptr_stub);
7566+ }
7667+
7768 //*************************************************************************
7869 /// Set from instance method (Run time).
7970 //*************************************************************************
80- @@ -494 ,6 +532,19 @@ namespace etl
71+ @@ -551 ,6 +585,18 @@ namespace etl
8172 return *this;
8273 }
8374
@@ -86,59 +77,38 @@ index 288c3a5..c7d41e2 100644
8677+ // Converts to a function pointer to avoid storing a dangling pointer
8778+ // to a destroyed temporary.
8879+ //*************************************************************************
89- + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, TReturn(*)(TArgs...) >::value, int> = 0>
80+ + template <typename TLambda, etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_reference<TLambda>::value && etl::is_convertible<TLambda, function_ptr >::value, int> = 0>
9081+ delegate& operator =(TLambda&& instance) ETL_NOEXCEPT
9182+ {
92- + TReturn(*fp)(TArgs...) = static_cast<TReturn(*)(TArgs...)>(instance);
93- + assign(reinterpret_cast<void*>(fp), function_ptr_stub);
83+ + assign(static_cast<function_ptr>(instance), function_ptr_stub);
9484+ return *this;
9585+ }
9686+
9787 //*************************************************************************
9888 /// Checks equality.
9989 //*************************************************************************
100- @@ -679,6 +730,16 @@ namespace etl
101- return (Method)(etl::forward<TArgs>(args)...);
102- }
103-
104- + //*************************************************************************
105- + /// Stub call for a runtime function pointer stored in the object field.
106- + //*************************************************************************
107- + static TReturn function_ptr_stub(void* object, TArgs... args)
108- + {
109- + ETL_STATIC_ASSERT(sizeof(void*) >= sizeof(TReturn(*)(TArgs...)), "etl::delegate: function pointer too large to store in object field");
110- + TReturn(*fp)(TArgs...) = reinterpret_cast<TReturn(*)(TArgs...)>(object);
111- + return fp(etl::forward<TArgs>(args)...);
112- + }
113- +
114- //*************************************************************************
115- /// Stub call for a lambda or functor function.
116- //*************************************************************************
11790diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp
118- index 57c52e8..3f6d8f5 100644
91+ index f990e115..da48ce68 100644
11992--- a/test/test_delegate.cpp
12093+++ b/test/test_delegate.cpp
121- @@ -634,16 +634,55 @@ namespace
122- #endif
94+ @@ -679,6 +679,58 @@ namespace
95+ CHECK_EQUAL(result, VALUE1 + VALUE2);
96+ }
12397
124- //*************************************************************************
125- - TEST_FIXTURE(SetupFixture, test_lambda_int_create)
126- + TEST_FIXTURE(SetupFixture, test_construct_from_rvalue_non_capturing_lambda)
127- {
128- - auto lambda = [](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); };
98+ + //*************************************************************************
99+ + TEST_FIXTURE(SetupFixture, test_construct_from_rvalue_non_capturing_lambda_implicit)
100+ + {
129101+ etl::delegate<int(int, int)> d([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j; });
130-
131- - etl::delegate<void(int, int)> d(lambda);
102+ +
132103+ int result = d(VALUE1, VALUE2);
133-
134- - d(VALUE1, VALUE2);
104+ +
135105+ CHECK(function_called == FunctionCalled::Lambda_Called);
136106+ CHECK(parameter_correct);
137107+ CHECK_EQUAL(result, VALUE1 + VALUE2);
138108+ }
139109+
140110+ //*************************************************************************
141- + TEST_FIXTURE(SetupFixture, test_assign_from_rvalue_non_capturing_lambda )
111+ + TEST_FIXTURE(SetupFixture, test_assign_from_rvalue_non_capturing_lambda_implicit )
142112+ {
143113+ etl::delegate<int(int, int)> d;
144114+
@@ -152,7 +122,7 @@ index 57c52e8..3f6d8f5 100644
152122+ }
153123+
154124+ //*************************************************************************
155- + TEST_FIXTURE(SetupFixture, test_create_from_rvalue_non_capturing_lambda )
125+ + TEST_FIXTURE(SetupFixture, test_create_from_rvalue_non_capturing_lambda_implicit )
156126+ {
157127+ auto d = etl::delegate<int(int, int)>::create([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 5; });
158128+
@@ -164,20 +134,22 @@ index 57c52e8..3f6d8f5 100644
164134+ }
165135+
166136+ //*************************************************************************
167- + TEST_FIXTURE(SetupFixture, test_set_from_rvalue_non_capturing_lambda_returning_int )
137+ + TEST_FIXTURE(SetupFixture, test_set_from_rvalue_non_capturing_lambda_implicit )
168138+ {
169139+ etl::delegate<int(int, int)> d;
170140+
171141+ d.set([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 6; });
172142+
173143+ int result = d(VALUE1, VALUE2);
174-
175- CHECK(function_called == FunctionCalled::Lambda_Called);
176- CHECK(parameter_correct);
144+ +
145+ + CHECK(function_called == FunctionCalled::Lambda_Called);
146+ + CHECK(parameter_correct);
177147+ CHECK_EQUAL(result, VALUE1 + VALUE2 + 6);
178- }
179-
180- #if ETL_USING_CPP17
148+ + }
149+ +
150+ //*************************************************************************
151+ TEST_FIXTURE(SetupFixture, test_assign_from_rvalue_non_capturing_lambda)
152+ {
181153- -
182- 2.43 .0
154+ 2.51 .0
183155
0 commit comments