Open positron96 opened 1 month ago
I'll take a look to see if it is possible.
I've been doing C++ for over 20 years, and it still manages to surprise me sometimes, especially convoluted template meta-programming!
I've had a go at this, and the fundamental problem is that you cannot explicitly declare the template parameters for a constructor. They must be deduced from the constructor's argument. This works fine for a lambda or functor as there is an instance argument to pass in. This is not possible for a freestanding function.
The only way to simplify construction is to make a lambda or functor wrapper around the free function.
auto my_function_lambda = []() { my_function(); };
add_callback(my_delegate_type(my_function_lambda));
With optimisation, the resulting code is still very efficient, even with the lambda and delegate.
For this code and -O1 optimisation, the lambda and delegate reduce to a direct call of the free function.
#include <iostream>
#include "etl/delegate.h"
void free_void()
{
std::cout << "free_void/n";
}
int main()
{
constexpr auto lambda = []() { free_void(); };
auto d = etl::delegate<void(void)>(lambda);
d();
}
.LC0:
.string "free_void/n"
free_void():
sub rsp, 8
mov edx, 11
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
add rsp, 8
ret
main:
sub rsp, 8
call free_void()
mov eax, 0
add rsp, 8
ret
Hi, thanks for looking into this! While you clearly show that adding lambda has no impact on performance, I would argue that it makes source code nosier still, meaning it hides the simple intent of adding a callback by introducing implementation-related entities.
the fundamental problem is that you cannot explicitly declare the template parameters for a constructor. They must be deduced from the constructor's argument.
Do you know if it's possible to explicitly cast a function (pointer) to a type to make compiler deduce template parameters? E.g.:
my_delegate_t callback{my_delegate_t::fn_t(my_function)};
where fn_t
is defined as something like
template <typename TReturn, typename... TParams>
class delegate<TReturn(TParams...)> {
using fn_t = TReturn(TParams...);
}
I tried to play with library code, but every time the compiler complained.
However in terms of verbosity this already approaches existing solution with ::create<>
I had a play this morning with creating a make_delgate
template function, which seemed to work, although it only works for C++14 and above.
add_callback(etl::make_delegate<my_function>());
I have worked out three etl::make_delegate
functions to simplify delegate construction for free and member functions.
Note: These are >= C++14 only.
void free_int(int);
struct S
{
void member(int);
void member_const(int) const;
}
static S s;
auto d1 = etl::make_delegate<free_int>();
auto d2 = etl::make_delegate<S, &S::member, s>();
auto d3 = etl::make_delegate<S, &S::member_const, s>();
auto d4 = etl::make_delegate<S, &S::member>(s);
auto d5 = etl::make_delegate<S, &S::member_const>(s);
So, compared to existing solution, this saves an extra template specialization, right?
auto d = etl::delegate<int(int)>::create<free_func>();
auto d = etl::delegate<int(int)>::create<Test, test, &Test::member_function>();
Yes, that's correct. make_delegate
deduces the function signature from the function pointer, so that it doesn't have to be explicitly declared.
Ok. Doesn't completely solve it, but this will make using delegates easier!
Actually, I've discovered that the syntax is only valid for C++17 and up.
At the moment, you construct a delegate with a freestanding function like this:
my_delegate_type::create<my_function>()
, and use it like this:add_callback(my_delegate_type::create<my_function>())
.It would be nice to have an implicit constructor for this case, then this
::create
distraction could be omitted and simplified to justadd_callback(my_function)
.As I understand, this syntax is possible for functors and lambdas, but not for freestanding functions. There are probably obstructions to doing this, otherwise you'd probably have implemented it long ago, but my c++-fu is not strong enough to understand it.