Is it possible to nest captureless lambdas when passing them around in C++?
From what I can gather, seems to not be the case; rather, it seems you'd have to make a template class, and define static functions, and then pass those around.
Related to this post, for pybind11
: Possible to have logical operations (e.g. `ndarray.__eq__`) not return `bool` in NumPy?
Basically, I want to see if I can avoid macros and still provide stateless functions (without any type-erased void* data
shenanigans) to be used with the Numpy UFunc API.
I'm aiming for stateless, because it appears the API has some functions that can have the void* data
, which I could use to pass another function pointer (or erased lambda with capture), but some of them appear not to.
Here's my hacking:
// Goal: Wrap a function of type `Func` without capture.
typedef void (*Func)();
// NOPE: `b` ain't stateless.
Func wrap(Func a) {
return [a]() { a(); };
}
int main() {
Func a = []() { cout << "A1\n"; };
wrap(a)();
}
// KINDA: Compiles, but `a` wasn't a parameter :(
// - Could use arrays a constexpr indices to fake it :( * 2
int main() {
static const Func a = []() { cout << "A1\n"; };
Func b = []() { a(); };
b();
}
// YUP-ish: Works, cannot deal with lambdas :(
template <Func a>
Func wrap() {
return []() { a(); };
}
// - Cannot be in a class in function scope :(
void a2() { cout << "A2\n"; }
int main() {
Func b = wrap<tmp::a2>();
b();
}
// NOPE: Lambda doesn't have default constructor (but what else would it do???)
int main() {
auto a = []() { cout << "A3\n"; };
using A = decltype(a);
Func b = []() { A a{}; a(); };
b();
}
I see there's some new stuff coming in for constexpr
lambdas, but that seems to be more about the return value being constexpr
, not the definition of the lambda itself (so that it could be passed as a template argument or whatever).
You can copy a lambda into a static variable:
template<typename F>
auto wrap(F a) {
static auto F b = std::move(a);
return []() { b(); };
}
Each lambda has a different type, so for each lambda, a new static variable is created, since the function is templated.
Note that it only work if the passed callable has a different type. If you only use stateless function object, you won't have any problems.
As a safe guard, you can also ensure only captureless lambda are sent:
template<typename F, std::void_t<
decltype(+std::declval<F>()),
decltype(&F::operator())
>* = nullptr>
auto wrap(F a) {
static auto F b = std::move(a);
return []() { b(); };
}
The sfinae expression looks for the unary operator+
that with catpureless lambda, and it also look for the presence of a operator()
member of the lambda.