在同一个函数中有 2 个 Lambda 表达式相互调用
Have 2 Lambda expressions within the same function call each other
我一直在为一个项目使用 SFML,我试图在仍然使用 SFML 游戏循环的同时进入暂停状态。长话短说,我一直在尝试在同一个函数中使用 2 个 Lambda 表达式来在我有 运行ning 的暂停和 运行 状态之间切换。下面是一些示例代码:
int main() {
//some stuff here...
auto pauseloop = [&] {
//do some stuff here...
runloop();
}
auto runloop = [&] {
//some more code, include SFML game loop here...
if (paused)
pauseloop();
}
runloop();
}
起初工作正常,但 运行 几乎立即陷入了一个问题:从 runloop()
中调用 pauseloop()
时,我无法从 runloop()
中调用=12=]。我试图在我的 main()
函数之上添加一些效果:
auto pauseloop = [&] {}; //initializer
auto runloop = [&] {}; //initializer
但是,经过一些广泛的测试和调试后,这会导致 main()
函数上方的 runloop()
Lambda 表达式在从 pauseloop()
调用时被调用,但是 intended, fully declared, runloop()
在main()
函数结束时调用runloop()
首先,有没有办法让同一个函数中的两个Lambda表达式相互调用?第二,为什么 pauseloop()
表达式调用 runloop()
的初始化版本,而 main()
末尾的 runloop()
调用预期的版本?根据我的最佳猜测,我会假设因为 runloop()
和 pauseloop()
都在调用任何一个之前被初始化定义,所以 pauseloop()
会调用声明的版本,因为它已被读入它的全部并正确地覆盖了初始化程序,但与实际发生的情况相比,这似乎完全不对劲。提前致谢!
编辑 1:简而言之,上述尝试不适用于预期的解决方案(让 Lambda 表达式“a”调用 Lambda 表达式“b”,反之亦然)。我将如何编写两个可以的 Lambda 表达式?
只要你的 lambda 有捕获,而且看起来像你的,你就不能让它们直接相互调用,因为当你声明它们时存在循环依赖。要解决此问题,您可以将它们分配给指针。
如果您没有捕获,编译器会简单地为您的 lambda 生成匿名函数,您可以像这样将它们分配给函数指针:
void (*pauseloop)() = nullptr;
void (*runloop)() = nullptr;
pauseloop = []() {
//...
runloop();
//...
};
runloop = []() {
//...
pauseloop();
//...
};
由于您确实有捕获,解决方案非常相似,但您会改用 std::function
。实际上,由于您也可以将 std::function
与函数一起使用,因此无论如何这是我的建议:
std::function<void()> pauseloop, runloop;
pauseloop = [&]() {
// ...
runloop();
// ...
};
runloop = [&]() {
// ...
pauseloop();
// ...
};
请注意,在这两种情况下,仅通过值捕获函数指针/std::function
是不够的。至少有一个 lambda 表达式必须通过引用捕获它或将其用作全局变量。否则它只会捕获初始 nullptr
值。
正如您所发现的,通过捕获简单地使用它们中的每一个都会产生循环依赖。另一种方法是将它们作为参数传递。
让我们从 runloop
开始。您可以将其作为参数传递,而不是通过捕获获取 pauseloop
:
auto runloop = [&](auto const& pauseloop) {
if (pause) {
pauseloop();
}
};
以后你可以这样称呼它:runloop(pauseloop);
类似地,您可以给 pauseloop
一个参数:
auto pauseloop = [&](auto const& runloop) {
runloop();
};
但是,现在在 pauseloop
中,您正在调用 runloop()
,尽管 runloop
实际上需要一个参数。同样,在 runloop
中,尽管 pauseloop
也需要一个参数,但您正在调用 pauseloop()
。要修复它,您还可以将它们中应该调用的参数传递给外部函数:
auto runloop = [&](auto const& runloop, auto const& pauseloop) {
if (pause) {
pauseloop(runloop, pauseloop);
}
};
auto pauseloop = [&](auto const& runloop, auto const& pauseloop) {
runloop(runloop, pauseloop);
};
现在,您可以这样调用 runloop
和 pauseloop
:
runloop(runloop, pauseloop);
pauseloop(runloop, pauseloop);
但是,最终的调用界面非常冗长。为了更简单,您可以将接口和实现分开:
auto _runloop_impl = [&](auto const& runloop, auto const& pauseloop) {
if (pause) {
pauseloop(runloop, pauseloop);
}
};
auto _pauseloop_impl = [&](auto const& runloop, auto const& pauseloop) {
runloop(runloop, pauseloop);
};
auto runloop = [&] {
_runloop_impl(_runloop_impl, _pauseloop_impl);
};
auto pauseloop = [&] {
_pauseloop_impl(_runloop_impl, _pauseloop_impl);
};
现在,您可以简单地称呼它们:
runloop();
pauseloop();
- 注意,直接调用lambda只需要定义接口
如果您有许多不同类型的循环要添加到逻辑中,您可以使用参数包轻松传递所有参数:
auto _runloop_impl = [&](auto const& ... loops) {
auto loops_tuple = std::tuple{std::cref(loops)...};
switch(state) {
case State::PAUSE:
std::get<1>(loops_tuple)(loops...);
break;
case State::MENU:
std::get<2>(loops_tuple)(loops...);
break;
case State::SHOP:
std::get<3>(loops_tuple)(loops...);
break;
}
};
auto _pauseloop_impl = [&](auto const& ... loops) {
auto loops_tuple = std::tuple{std::cref(loops)...};
std::get<0>(loops_tuple)(loops...);
};
////////////////////////////////////////////////////
//
// ... similar implementations for other loops
//
////////////////////////////////////////////////////
auto runloop = [&] {
_runloop_impl(
_runloop_impl,
_pauseloop_impl,
_menuloop_impl,
_shoploop_impl
)
};
int main() {
runloop();
}
我一直在为一个项目使用 SFML,我试图在仍然使用 SFML 游戏循环的同时进入暂停状态。长话短说,我一直在尝试在同一个函数中使用 2 个 Lambda 表达式来在我有 运行ning 的暂停和 运行 状态之间切换。下面是一些示例代码:
int main() {
//some stuff here...
auto pauseloop = [&] {
//do some stuff here...
runloop();
}
auto runloop = [&] {
//some more code, include SFML game loop here...
if (paused)
pauseloop();
}
runloop();
}
起初工作正常,但 运行 几乎立即陷入了一个问题:从 runloop()
中调用 pauseloop()
时,我无法从 runloop()
中调用=12=]。我试图在我的 main()
函数之上添加一些效果:
auto pauseloop = [&] {}; //initializer
auto runloop = [&] {}; //initializer
但是,经过一些广泛的测试和调试后,这会导致 main()
函数上方的 runloop()
Lambda 表达式在从 pauseloop()
调用时被调用,但是 intended, fully declared, runloop()
在main()
函数结束时调用runloop()
首先,有没有办法让同一个函数中的两个Lambda表达式相互调用?第二,为什么 pauseloop()
表达式调用 runloop()
的初始化版本,而 main()
末尾的 runloop()
调用预期的版本?根据我的最佳猜测,我会假设因为 runloop()
和 pauseloop()
都在调用任何一个之前被初始化定义,所以 pauseloop()
会调用声明的版本,因为它已被读入它的全部并正确地覆盖了初始化程序,但与实际发生的情况相比,这似乎完全不对劲。提前致谢!
编辑 1:简而言之,上述尝试不适用于预期的解决方案(让 Lambda 表达式“a”调用 Lambda 表达式“b”,反之亦然)。我将如何编写两个可以的 Lambda 表达式?
只要你的 lambda 有捕获,而且看起来像你的,你就不能让它们直接相互调用,因为当你声明它们时存在循环依赖。要解决此问题,您可以将它们分配给指针。
如果您没有捕获,编译器会简单地为您的 lambda 生成匿名函数,您可以像这样将它们分配给函数指针:
void (*pauseloop)() = nullptr;
void (*runloop)() = nullptr;
pauseloop = []() {
//...
runloop();
//...
};
runloop = []() {
//...
pauseloop();
//...
};
由于您确实有捕获,解决方案非常相似,但您会改用 std::function
。实际上,由于您也可以将 std::function
与函数一起使用,因此无论如何这是我的建议:
std::function<void()> pauseloop, runloop;
pauseloop = [&]() {
// ...
runloop();
// ...
};
runloop = [&]() {
// ...
pauseloop();
// ...
};
请注意,在这两种情况下,仅通过值捕获函数指针/std::function
是不够的。至少有一个 lambda 表达式必须通过引用捕获它或将其用作全局变量。否则它只会捕获初始 nullptr
值。
正如您所发现的,通过捕获简单地使用它们中的每一个都会产生循环依赖。另一种方法是将它们作为参数传递。
让我们从 runloop
开始。您可以将其作为参数传递,而不是通过捕获获取 pauseloop
:
auto runloop = [&](auto const& pauseloop) {
if (pause) {
pauseloop();
}
};
以后你可以这样称呼它:runloop(pauseloop);
类似地,您可以给 pauseloop
一个参数:
auto pauseloop = [&](auto const& runloop) {
runloop();
};
但是,现在在 pauseloop
中,您正在调用 runloop()
,尽管 runloop
实际上需要一个参数。同样,在 runloop
中,尽管 pauseloop
也需要一个参数,但您正在调用 pauseloop()
。要修复它,您还可以将它们中应该调用的参数传递给外部函数:
auto runloop = [&](auto const& runloop, auto const& pauseloop) {
if (pause) {
pauseloop(runloop, pauseloop);
}
};
auto pauseloop = [&](auto const& runloop, auto const& pauseloop) {
runloop(runloop, pauseloop);
};
现在,您可以这样调用 runloop
和 pauseloop
:
runloop(runloop, pauseloop);
pauseloop(runloop, pauseloop);
但是,最终的调用界面非常冗长。为了更简单,您可以将接口和实现分开:
auto _runloop_impl = [&](auto const& runloop, auto const& pauseloop) {
if (pause) {
pauseloop(runloop, pauseloop);
}
};
auto _pauseloop_impl = [&](auto const& runloop, auto const& pauseloop) {
runloop(runloop, pauseloop);
};
auto runloop = [&] {
_runloop_impl(_runloop_impl, _pauseloop_impl);
};
auto pauseloop = [&] {
_pauseloop_impl(_runloop_impl, _pauseloop_impl);
};
现在,您可以简单地称呼它们:
runloop();
pauseloop();
- 注意,直接调用lambda只需要定义接口
如果您有许多不同类型的循环要添加到逻辑中,您可以使用参数包轻松传递所有参数:
auto _runloop_impl = [&](auto const& ... loops) {
auto loops_tuple = std::tuple{std::cref(loops)...};
switch(state) {
case State::PAUSE:
std::get<1>(loops_tuple)(loops...);
break;
case State::MENU:
std::get<2>(loops_tuple)(loops...);
break;
case State::SHOP:
std::get<3>(loops_tuple)(loops...);
break;
}
};
auto _pauseloop_impl = [&](auto const& ... loops) {
auto loops_tuple = std::tuple{std::cref(loops)...};
std::get<0>(loops_tuple)(loops...);
};
////////////////////////////////////////////////////
//
// ... similar implementations for other loops
//
////////////////////////////////////////////////////
auto runloop = [&] {
_runloop_impl(
_runloop_impl,
_pauseloop_impl,
_menuloop_impl,
_shoploop_impl
)
};
int main() {
runloop();
}