mirror of
https://github.com/ETLCPP/etl.git
synced 2026-07-30 16:26:17 +08:00
Add constexpr support for delegate construction and assignment (#1476)
* feat: Add constexpr support for delegate construction and assignment in C++14 and C++20 * docs: add more docstring coverage #1473 * docs: note constexpr availability for function-pointer overloads Update docs/callbacks/delegate.md to reflect the new ETL_CONSTEXPR14 annotations on delegate(function_ptr) and create(function_ptr), and add a paragraph to the Constexpr section explaining which function-pointer APIs are usable in constant expressions at C++14 vs C++20. #1473 --------- Co-authored-by: Roland Reichwein <Roland.Reichwein@bmw.de>
This commit is contained in:
parent
05010aebf8
commit
e6a7642121
@ -161,6 +161,19 @@ constexpr auto d = etl::delegate<int(int)>::create<Test, test, &Test::member_fun
|
||||
All create functions are `constexpr` under C++14.
|
||||
All create functions are `[[nodiscard]]` under C++17.
|
||||
|
||||
`>=TBC`
|
||||
The runtime function-pointer overloads `delegate(function_ptr)` and
|
||||
`create(function_ptr)` are usable in constant expressions from C++14.
|
||||
The mutating overloads `set(function_ptr)` and `operator=(function_ptr)` are
|
||||
usable in constant expressions from C++20 (they switch the active member of
|
||||
an internal union, which became permitted in constant expressions in C++20
|
||||
per [P1330R0](https://wg21.link/P1330R0)).
|
||||
|
||||
```cpp
|
||||
constexpr auto d1 = etl::delegate<int(int)>(&global);
|
||||
constexpr auto d2 = etl::delegate<int(int)>::create(&global);
|
||||
```
|
||||
|
||||
### Calling the delegate
|
||||
|
||||
The delegate may be called as a function with the defined parameter signature.
|
||||
@ -195,11 +208,12 @@ Copy constructs a delegate.
|
||||
---
|
||||
|
||||
```cpp
|
||||
delegate(TReturn (*)(TArgs...) fp) ETL_NOEXCEPT
|
||||
explicit ETL_CONSTEXPR14 delegate(TReturn (*)(TArgs...) fp) ETL_NOEXCEPT
|
||||
```
|
||||
**Description**
|
||||
Constructs from a run-time function pointer, or non-capturing lambda converted to a function pointer.
|
||||
*From `20.47.0`*
|
||||
*Constexpr from `TBC`*
|
||||
|
||||
## Creation
|
||||
|
||||
@ -218,11 +232,12 @@ A constructed delegate.
|
||||
|
||||
```cpp
|
||||
ETL_NODISCARD
|
||||
static delegate create(function_ptr fp) ETL_NOEXCEPT
|
||||
static ETL_CONSTEXPR14 delegate create(function_ptr fp) ETL_NOEXCEPT
|
||||
```
|
||||
**Description**
|
||||
Create from a run-time function pointer.
|
||||
*From `20.47.0`*
|
||||
*Constexpr from `TBC`*
|
||||
|
||||
**Returns**
|
||||
A constructed delegate.
|
||||
|
||||
@ -121,12 +121,18 @@ namespace etl
|
||||
{
|
||||
private:
|
||||
|
||||
using object_ptr = void*;
|
||||
/// Type-erased pointer to the bound object instance.
|
||||
using object_ptr = void*;
|
||||
|
||||
/// Pointer type for a runtime-bound free function with the delegate's signature.
|
||||
using function_ptr = TReturn (*)(TArgs...);
|
||||
|
||||
public:
|
||||
|
||||
using return_type = TReturn;
|
||||
/// The delegate's return type.
|
||||
using return_type = TReturn;
|
||||
|
||||
/// Type list of the delegate's argument types.
|
||||
using argument_types = etl::type_list<TArgs...>;
|
||||
|
||||
//*************************************************************************
|
||||
@ -137,12 +143,12 @@ namespace etl
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Copy constructor.
|
||||
/// Copy constructor.
|
||||
//*************************************************************************
|
||||
ETL_CONSTEXPR14 delegate(const delegate& other) = default;
|
||||
|
||||
//*************************************************************************
|
||||
// Construct from lambda or functor.
|
||||
/// Construct from a lambda or functor.
|
||||
//*************************************************************************
|
||||
template < typename TLambda, typename = etl::enable_if_t< etl::is_class<TLambda>::value && !is_delegate<TLambda>::value, void>>
|
||||
ETL_CONSTEXPR14 delegate(TLambda& instance) ETL_NOEXCEPT
|
||||
@ -151,7 +157,7 @@ namespace etl
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Construct from const lambda or functor.
|
||||
/// Construct from a const lambda or functor.
|
||||
//*************************************************************************
|
||||
template < typename TLambda, typename = etl::enable_if_t< etl::is_class<TLambda>::value && !is_delegate<TLambda>::value, void>>
|
||||
ETL_CONSTEXPR14 delegate(const TLambda& instance) ETL_NOEXCEPT
|
||||
@ -160,8 +166,8 @@ namespace etl
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Delete construction from rvalue reference lambda or functor.
|
||||
// Excludes non-capturing lambdas convertible to a function pointer.
|
||||
/// Delete construction from an rvalue reference lambda or functor.
|
||||
/// Excludes non-capturing lambdas convertible to a function pointer.
|
||||
//*************************************************************************
|
||||
template <typename TLambda,
|
||||
typename = etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_same<etl::delegate<TReturn(TArgs...)>, TLambda>::value
|
||||
@ -170,11 +176,11 @@ namespace etl
|
||||
ETL_CONSTEXPR14 delegate(TLambda&& instance) = delete;
|
||||
|
||||
//*************************************************************************
|
||||
// Construct from a function pointer.
|
||||
/// Construct from a function pointer.
|
||||
//*************************************************************************
|
||||
explicit delegate(function_ptr fp) ETL_NOEXCEPT
|
||||
explicit ETL_CONSTEXPR14 delegate(function_ptr fp) ETL_NOEXCEPT
|
||||
: invocation(fp, function_ptr_stub)
|
||||
{
|
||||
assign(fp, function_ptr_stub);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
@ -208,10 +214,10 @@ namespace etl
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Create from a function pointer.
|
||||
/// Create from a function pointer.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
static delegate create(function_ptr fp) ETL_NOEXCEPT
|
||||
static ETL_CONSTEXPR14 delegate create(function_ptr fp) ETL_NOEXCEPT
|
||||
{
|
||||
return delegate(fp, function_ptr_stub);
|
||||
}
|
||||
@ -333,9 +339,9 @@ namespace etl
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Set from a function pointer.
|
||||
/// Set from a function pointer.
|
||||
//*************************************************************************
|
||||
void set(function_ptr fp) ETL_NOEXCEPT
|
||||
ETL_CONSTEXPR14 void set(function_ptr fp) ETL_NOEXCEPT
|
||||
{
|
||||
assign(fp, function_ptr_stub);
|
||||
}
|
||||
@ -530,9 +536,9 @@ namespace etl
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Create from a function pointer.
|
||||
/// Assign from a function pointer.
|
||||
//*************************************************************************
|
||||
delegate& operator=(function_ptr fp) ETL_NOEXCEPT
|
||||
ETL_CONSTEXPR14 delegate& operator=(function_ptr fp) ETL_NOEXCEPT
|
||||
{
|
||||
if (fp == ETL_NULLPTR)
|
||||
{
|
||||
@ -623,8 +629,11 @@ namespace etl
|
||||
//*************************************************************************
|
||||
struct invocation_element
|
||||
{
|
||||
/// Signature of the dispatch stub used to invoke the bound callable.
|
||||
using stub_type = TReturn (*)(const invocation_element&, TArgs...);
|
||||
|
||||
//***********************************************************************
|
||||
/// Constructs an empty invocation element (no bound callable).
|
||||
//***********************************************************************
|
||||
ETL_CONSTEXPR14 invocation_element() ETL_NOEXCEPT
|
||||
: ptr(object_ptr(ETL_NULLPTR))
|
||||
@ -632,6 +641,9 @@ namespace etl
|
||||
{
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Constructs an invocation element with only a stub (used for
|
||||
/// compile-time bindings that need no associated object pointer).
|
||||
//***********************************************************************
|
||||
ETL_CONSTEXPR14 invocation_element(stub_type stub_) ETL_NOEXCEPT
|
||||
: ptr()
|
||||
@ -639,6 +651,8 @@ namespace etl
|
||||
{
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Constructs an invocation element bound to an object pointer and stub.
|
||||
//***********************************************************************
|
||||
ETL_CONSTEXPR14 invocation_element(object_ptr object_, stub_type stub_) ETL_NOEXCEPT
|
||||
: ptr(object_)
|
||||
@ -646,6 +660,8 @@ namespace etl
|
||||
{
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Constructs an invocation element bound to a function pointer and stub.
|
||||
//***********************************************************************
|
||||
ETL_CONSTEXPR14 invocation_element(function_ptr fp_, stub_type stub_) ETL_NOEXCEPT
|
||||
: ptr(fp_)
|
||||
@ -653,37 +669,48 @@ namespace etl
|
||||
{
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Equality compares the stub and the active pointer member.
|
||||
//***********************************************************************
|
||||
ETL_CONSTEXPR14 bool operator==(const invocation_element& rhs) const ETL_NOEXCEPT
|
||||
{
|
||||
return (rhs.stub == stub) && ((stub == function_ptr_stub) ? (rhs.ptr.fp == ptr.fp) : (rhs.ptr.object == ptr.object));
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Inequality compares the stub and the active pointer member.
|
||||
//***********************************************************************
|
||||
ETL_CONSTEXPR14 bool operator!=(const invocation_element& rhs) const ETL_NOEXCEPT
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Resets the invocation element to the empty state.
|
||||
//***********************************************************************
|
||||
ETL_CONSTEXPR14 void clear() ETL_NOEXCEPT
|
||||
{
|
||||
stub = ETL_NULLPTR;
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
/// Storage holding either an object pointer or a function pointer.
|
||||
//***********************************************************************
|
||||
union ptr_type
|
||||
{
|
||||
/// Default-constructs with a null object pointer as the active member.
|
||||
ETL_CONSTEXPR14 ptr_type() ETL_NOEXCEPT
|
||||
: object(ETL_NULLPTR)
|
||||
{
|
||||
}
|
||||
|
||||
/// Constructs with the object pointer as the active member.
|
||||
ETL_CONSTEXPR14 ptr_type(object_ptr object_) ETL_NOEXCEPT
|
||||
: object(object_)
|
||||
{
|
||||
}
|
||||
|
||||
/// Constructs with the function pointer as the active member.
|
||||
ETL_CONSTEXPR14 ptr_type(function_ptr fp_) ETL_NOEXCEPT
|
||||
: fp(fp_)
|
||||
{
|
||||
@ -697,6 +724,7 @@ namespace etl
|
||||
stub_type stub;
|
||||
};
|
||||
|
||||
/// Function pointer type used to dispatch through the invocation stub.
|
||||
using stub_type = typename invocation_element::stub_type;
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
@ -2125,6 +2125,48 @@ namespace
|
||||
(void)d;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP14
|
||||
//*************************************************************************
|
||||
// Verify that constructing / creating / setting / assigning a delegate
|
||||
// from a free function pointer is usable in a constant expression.
|
||||
//*************************************************************************
|
||||
TEST(test_constexpr_function_ptr_construction)
|
||||
{
|
||||
using delegate_type = etl::delegate<void(void)>;
|
||||
|
||||
constexpr delegate_type d1(&free_void);
|
||||
constexpr delegate_type d2 = delegate_type::create(&free_void);
|
||||
|
||||
static_assert(d1 == d2, "constexpr-constructed delegates should compare equal");
|
||||
|
||||
#if ETL_USING_CPP20
|
||||
// Exercise constexpr set() and operator= in a constant-evaluated
|
||||
// mutating context. Requires C++20 because the underlying union must
|
||||
// switch its active member, which is only permitted in constant
|
||||
// expressions from C++20 onwards (P1330R0).
|
||||
constexpr auto make_via_set = []() constexpr
|
||||
{
|
||||
delegate_type d;
|
||||
d.set(&free_void);
|
||||
return d;
|
||||
};
|
||||
|
||||
constexpr auto make_via_assign = []() constexpr
|
||||
{
|
||||
delegate_type d;
|
||||
d = &free_void;
|
||||
return d;
|
||||
};
|
||||
|
||||
constexpr delegate_type d3 = make_via_set();
|
||||
constexpr delegate_type d4 = make_via_assign();
|
||||
|
||||
static_assert(d3 == d1, "");
|
||||
static_assert(d4 == d1, "");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user