What do you think of this code?
#include <utility>
namespace
{
template <typename F, int I, typename L, typename R, typename ...A>
inline F cify(L&& l, R (*)(A...) noexcept(noexcept(
::std::declval<F>()(::std::declval<A>()...))))
{
static thread_local L l_(::std::forward<L>(l));
static thread_local bool full;
if (full)
{
l_.~L();
new (static_cast<void*>(&l_)) L(::std::forward<L>(l));
}
else
{
full = true;
}
struct S
{
static R f(A... args) noexcept(noexcept(
::std::declval<F>()(::std::forward<A>(args)...)))
{
return l_(::std::forward<A>(args)...);
}
};
return &S::f;
}
}
template <typename F, int I = 0, typename L>
F cify(L&& l)
{
return cify<F, I>(::std::forward<L>(l), F());
}
It can be used to supply a capturing lambda as a C callback:
int main()
{
int a;
auto const f(cify<void(*)()>([a]{::std::cout << "a: " << a << ::std::endl;}));
f();
return 0;
}
If you need the same callback across multiple threads, or when thread_local is unimplemented in your compiler, you can remove the thread_local
keyword.
To fix the problem in the answer, one can make use of the __COUNTER__
macro. It is only one of numerous possible solutions.
std::vector<void(*)()> callbacks;
template <int I>
void add_callback (int x)
{
callbacks.emplace_back(cify<void(*)(), I>([x] () { std::cout << x; }));
}
int main ()
{
add_callback<__COUNTER__>(1);
add_callback<__COUNTER__>(2);
for (auto& callback : callbacks) {
callback();
}
return 0;
}
I
serve exactly? If it's for ensuring the initialization ofl_
, doesn't theL
parameter already do that? The same functor type wouldn't needl_
to be reinitialized I would think. If everything here is actually guaranteed to work, then this is the first useful thing I've seen done with local classes, so good job. – chris Feb 4 at 19:39I
serves to create differing template instantiations, even thoughL
andF
might be the same. – user1095108 Feb 4 at 19:51