传递本地创建的 lambda 用于回调,然后超出范围
Passing a locally-created lambda for use in a callback, then going out of scope
我正在使用一些涉及回调机制的(有点像 C 语言的)库。我可以提供的回调函数将 void*
作为参数,因此您可以将任意内容传递给它们。为了这个问题,我们假设 lambda 不带任何参数,但它确实捕获了东西。
现在,我需要让我的回调函数调用一个 lambda - 它必须以某种方式通过 void *
获取这个 lambda,即我们有
void my_callback(void * arbitrary_stuff) {
/* magic... and somehow the lambda passed */
/* through `arbitrary_stuff` is invoked. */
}
// ...
template <T>
void adapted_add_callback(MagicTypeInvolvingT actual_callback) {
/* more magic */
libFooAddCallback(my_callback, something_based_on_actual_callback);
}
// ...
void baz();
void bar() {
int x;
adapted_add_callback([x]() { /* do something with x */ });
adapted_add_callback(baz);
}
我想知道用什么替换 magic
、more_magic
和 MagicTypeInvolvingT
。
除了这里的打字挑战,显然,我担心的是如何确保 lambda 封装的数据在堆栈上可用以供最终使用,否则我可能应该进行某种分段错误。
备注:
my_callback()
应该是同步的,因为它会在它所在的任何线程上执行 lambda,而 return 在 return 时执行。执行异步性的是 fooLibrary 或 lambda 本身。
你有几个问题。
一个是 你不能依赖于将 lambda 本身作为 void *
传递,所以你几乎需要传递一个指向 lambda 的指针(嗯,从 lambda 创建的闭包,如果你想要精确的话)。这意味着您需要确保 lambda 在回调完成之前保持有效。
第二个问题是关于这些捕获如何发生的问题 - 按值捕获还是按引用捕获?如果按值捕获,一切都很好。如果您通过引用捕获,您还需要确保您捕获的任何内容在回调完成之前仍然有效。如果您通过引用捕获一个全局变量,那通常应该没问题——但是如果您通过引用捕获一个局部变量,那么在调用 lambda 之前,局部变量(甚至可能)超出范围,使用引用将导致未定义的行为。
最直接的方法可能是(假设 C 函数保证只调用一次回调,并且 lambda 在回调点有效)
void my_callback(void * arbitrary_stuff) {
(*std::unique_ptr{ static_cast<std::function<void()>*>(arbitrary_stuff) })();
}
void adapted_add_callback( std::function<void()> actual_callback ) {
libFooAddCallback(my_callback, new auto( std::move(actual_callback) ) );
}
如果您不想要函数<>开销,您将需要实现自己的类型擦除...
我采用了类似于 的方式,但没有 std::function
的开销。您必须确保库只调用一次回调。
using Callback = void(*)(void*);
// Probes the type of the argument and generates a suitable cast & invoke stub
// Caution: self-destructs after use!
template <class F>
Callback cbkWrap(F &) {
return [](void *data) {
std::unique_ptr<F> retrieved(static_cast<F*>(data));
(*retrieved)();
};
}
// Moves the functor into a dynamically-allocated one
template <class F>
void *cbkFunc(F &f) {
return new F{std::move(f)};
}
int main() {
int x = 42;
auto lambda = [&x] { std::cout << x << '\n'; };
libFooAddCallback(cbkWrap(lambda), cbkFunc(lambda));
}
如果你能确保 lambda 比潜在调用更有效,你就可以摆脱动态内存分配并简单地传递一个指向它的指针 data
:
// Probes the type of the argument and generates a suitable cast & invoke stub
template <class F>
Callback cbkWrap(F &) {
return [](void *data) {
auto retrieved = static_cast<F*>(data);
(*retrieved)();
};
}
int main() {
int x = 42;
auto lambda = [&x] { std::cout << x << '\n'; };
libFooAddCallback(cbkWrap(lambda), &lambda);
}
不幸的是,如果不知道它会被调用多少次,就无法将 lamba 的所有权赋予库。
我正在使用一些涉及回调机制的(有点像 C 语言的)库。我可以提供的回调函数将 void*
作为参数,因此您可以将任意内容传递给它们。为了这个问题,我们假设 lambda 不带任何参数,但它确实捕获了东西。
现在,我需要让我的回调函数调用一个 lambda - 它必须以某种方式通过 void *
获取这个 lambda,即我们有
void my_callback(void * arbitrary_stuff) {
/* magic... and somehow the lambda passed */
/* through `arbitrary_stuff` is invoked. */
}
// ...
template <T>
void adapted_add_callback(MagicTypeInvolvingT actual_callback) {
/* more magic */
libFooAddCallback(my_callback, something_based_on_actual_callback);
}
// ...
void baz();
void bar() {
int x;
adapted_add_callback([x]() { /* do something with x */ });
adapted_add_callback(baz);
}
我想知道用什么替换 magic
、more_magic
和 MagicTypeInvolvingT
。
除了这里的打字挑战,显然,我担心的是如何确保 lambda 封装的数据在堆栈上可用以供最终使用,否则我可能应该进行某种分段错误。
备注:
my_callback()
应该是同步的,因为它会在它所在的任何线程上执行 lambda,而 return 在 return 时执行。执行异步性的是 fooLibrary 或 lambda 本身。
你有几个问题。
一个是 你不能依赖于将 lambda 本身作为 void *
传递,所以你几乎需要传递一个指向 lambda 的指针(嗯,从 lambda 创建的闭包,如果你想要精确的话)。这意味着您需要确保 lambda 在回调完成之前保持有效。
第二个问题是关于这些捕获如何发生的问题 - 按值捕获还是按引用捕获?如果按值捕获,一切都很好。如果您通过引用捕获,您还需要确保您捕获的任何内容在回调完成之前仍然有效。如果您通过引用捕获一个全局变量,那通常应该没问题——但是如果您通过引用捕获一个局部变量,那么在调用 lambda 之前,局部变量(甚至可能)超出范围,使用引用将导致未定义的行为。
最直接的方法可能是(假设 C 函数保证只调用一次回调,并且 lambda 在回调点有效)
void my_callback(void * arbitrary_stuff) {
(*std::unique_ptr{ static_cast<std::function<void()>*>(arbitrary_stuff) })();
}
void adapted_add_callback( std::function<void()> actual_callback ) {
libFooAddCallback(my_callback, new auto( std::move(actual_callback) ) );
}
如果您不想要函数<>开销,您将需要实现自己的类型擦除...
我采用了类似于 std::function
的开销。您必须确保库只调用一次回调。
using Callback = void(*)(void*);
// Probes the type of the argument and generates a suitable cast & invoke stub
// Caution: self-destructs after use!
template <class F>
Callback cbkWrap(F &) {
return [](void *data) {
std::unique_ptr<F> retrieved(static_cast<F*>(data));
(*retrieved)();
};
}
// Moves the functor into a dynamically-allocated one
template <class F>
void *cbkFunc(F &f) {
return new F{std::move(f)};
}
int main() {
int x = 42;
auto lambda = [&x] { std::cout << x << '\n'; };
libFooAddCallback(cbkWrap(lambda), cbkFunc(lambda));
}
如果你能确保 lambda 比潜在调用更有效,你就可以摆脱动态内存分配并简单地传递一个指向它的指针 data
:
// Probes the type of the argument and generates a suitable cast & invoke stub
template <class F>
Callback cbkWrap(F &) {
return [](void *data) {
auto retrieved = static_cast<F*>(data);
(*retrieved)();
};
}
int main() {
int x = 42;
auto lambda = [&x] { std::cout << x << '\n'; };
libFooAddCallback(cbkWrap(lambda), &lambda);
}
不幸的是,如果不知道它会被调用多少次,就无法将 lamba 的所有权赋予库。