Calling a stateless lambda without an instance (type only)
I am trying to write a wrapper for the "register callback" interface type from the C library. The problem is quite complicated because the library allows you to register the "variables" of a function by accepting a list of parameter definitions. Then, during the callback, the function is expected to retrieve its arguments from the list of type-erased arguments. Good old C ...
The interface I am trying to create is to accept any function, even a lambda, and automatically generate all mechanisms to register that function correctly, and also register the arguments and retrieve them during the callback. Therefore, the user only needs to enter:
register_callback("Callback Name", [](int i){return i+0.5;});
Now this is theoretically possible, but at some point I need to be able to call a lambda without any instance, but with access only to its type. I don't understand why, but the default constructor of the non-coreless (non-capturing) lambda is removed, so I can't just construct the default type of it. What I plan on doing is a very messy looking one, but should probably be OK to get around this:
template <class Func, class RET, class ...ARGS>
struct from_stateless {
static RET to_function(ARGS...args) {
RET (*f)(ARGS...) = *(Func *)(0);
return f(args...);
}
};
so it from_stateless<LAMBDA_TYPE, RET, ARGS...>::to_function
is actually a function pointer that I can call without an argument and more importantly something that I can pass as a PATTERN ARMATURE.
Under normal circumstances, the line RET (*f)(ARGS...) = *(Func *)(0);
would be suicidal, but it should actually be safe with this use case, right? After all, the function pointer resulting from the transformation could not, in any universe, possibly depend on the lambda instance.
So the question is, is it safe to do this if I make sure that the type is indeed stateless? Or am I missing something? Note that it RET (*f)(ARGS...) = *(Func *)(0);
will throw a compiler error if the closure accidentally slips.
CLARIFICATION: I can't just decompose the lambda on a function pointer and register it since the lambda signature is incompatible with the Register method. The register method expects a function with a signature: void (*)(int number_of_args, TypeErasedValue * arguments, TypeErasedValue * result)
So you see, I need (and already do), through template metaprogramming, generate a free function tuned to the lambda type to act as an adapter between the expected and the actual signatures.
source to share
Do you need specific behavior?
Add to
static Func state(Func* op=0){
static Func f=*op;
return f;
}
to your templated class. The first time you call this pass ptr to a lambda. 2nd time, extract the value for free.
static Func f
is created on the first call of the function: as long as the pointer is not null, we're fine. Each time it is called the pointer can be null and it doesn't use it as static
locals are only created once.
Name it "register" with a pointer to a lambda. Elsewhere (on a callback) call him to get a copy. The lambda is stateless, so the copy is free.
source to share
It becomes even easier than @Yakk if you take advantage of the fact that a lambda can see the local locators of static
its enclosing scope without having to grab them. Using this fact, you can wrap a user-supplied non-guaranteed lambda in your own non-guaranteed lambda with the required signature and convert it to a function pointer.
using callback = void (*)(int, TypeErasedValue*, TypeErasedValue*);
template <typename F>
callback stateless_to_callback(F f) {
static F static_f = f;
return [](int argc, TypeErasedValue* argv, TypeErasedValue* result) {
// extract arguments from argv
auto r = static_f( /* arguments... */ );
// store r in result
};
}
source to share
Assuming you have something to register a normal function
template <typename Ret, typename... Args>
void register_callback(const std::string& s, Ret(*)(Args...))
{
std::cout << "register function " << s << std::endl;
// Implementation
}
You can just explicitly convert your non-capturing lambda to a function pointer:
template <typename Lambda>
void register_callback(const std::string& s, const Lambda&f)
{
// convert non capturing lambda into function pointer
Register(s, +f);
}
or change your calling site to something like:
register_callback("Callback Name", +[](int i){return i + 0.5;});
Note. You can use *
instead +
for decay.
source to share