如何将封盖包裹在封盖中?

How can I wrap a closure in a closure?

提问人:Edward Falk 提问时间:2/8/2022 最后编辑:Edward Falk 更新时间:2/8/2022 访问量:231

问:

这是我的特定用例,但我相信还有其他用例:

我将回调闭包传递给可能在不同线程中运行的函数。闭包中的代码可能会引发异常。我不希望这个例外杀死我的程序。我很高兴抓住它并报告它。(是的,我知道这样做可能会导致资源泄漏;我现在忽略了这个问题。

我的代码中有数百个这样的闭包,所以我不想编辑它们中的每一个来添加 try...catch 子句。我宁愿在闭包周围有一个简短的包装。

我的示例代码如下所示:

#include <iostream>
#include <thread>

using namespace std;
     
int   
main()
{
    string str("Hello, world");

    // Program will die because of exception thrown in handler.
    context->invoke(
        [str](bool success) {
            cout << str << ": " << success << endl;
            throw exception();
        }
    );

    myThread.join();

    // I want my program to not die
    context->invoke(
        []{exceptionCatcher(
            [str](bool success) {
                cout << str << ": " << success << endl;
                throw exception();
            }
        );}
    );

    myThread2.join();

    return 0;
}

然后我的模板看起来像这样:exceptionCatcher

template<typename Func>
void exceptionCatcher(Func func)
{
    try {
        func();
    }
    catch (const exception& e) {
        cout << "Caught exception\n";
    }
}

除了编译器不喜欢这样。

c++ -std=c++17 -o tester tester.cpp
tester.cpp:40:14: error: variable 'str' cannot be implicitly captured in a lambda with no
      capture-default specified
            [str]{
             ^
tester.cpp:23:12: note: 'str' declared here
    string str("Hello, world");
           ^
tester.cpp:39:9: note: lambda expression begins here
        []{exceptionCatcher(

写什么魔法咒语?exceptionCatcher

复杂功能:有许多这样的“调用”函数。捕获的值和闭包的参数各不相同。是否可以编写一个处理所有这些情况的包装器?

C++ 闭包

评论

1赞 Remy Lebeau 2/8/2022
不应动态创建引发的异常。改为按值抛出它们:throw exception();
0赞 Edward Falk 2/8/2022
实际上,这几乎是另一个问题的主题:哪种形式更受欢迎?我想我有我的答案;谢谢。我已经相应地编辑了我的问题。
0赞 Edward Falk 2/8/2022
注意:我已经根据@Jason的建议更新了代码。错误消息现在不同了。
1赞 Remy Lebeau 2/8/2022
您没有在外部 lambda 中捕获,因此它在内部 lambda 中不可用。str
0赞 Edward Falk 2/8/2022
我更新了我的问题,以更好地展示我的用例。我的闭包可以捕获任何变量,并且它接受不同的参数。

答:

2赞 Jason 2/8/2022 #1

你的 lambda 不会是一个指针。它总是按值传递。

template <typename Func>
void exceptionCatcher(Func func)
{
   //...
}

评论

1赞 Edward Falk 2/8/2022
谢谢;这几乎奏效了。问题在于内部闭包捕获一个值 ()。仅当外部闭包捕获相同的变量时,这才有效。可悲的是,我怀疑我在那里别无选择。[str]
0赞 Remy Lebeau 2/8/2022
您实际上不需要使用 lambda 来调用 ,因为接受任何可调用对象,包括函数,例如:demo。或者:演示exceptionHandler()std::threadauto func = [str]{ ... }; myThread2(exceptionCatcher<decltype(func)>, func);void exceptionCatcher(std::function<void()> func) { ... func(); ... } thread myThread2(exceptionCatcher, [str]{ ... });
0赞 Edward Falk 2/8/2022
好的,它编译并在我的系统上运行。它取决于 std::thread 的构造函数,该构造函数分别采用函数指针和参数。我的实际用例是 std::thread 的包装器,我不确定包装器是否可以接受这些参数。在我测试它时待命。
0赞 Edward Falk 2/8/2022
我想我犯了过于简单化的罪。我将编辑我的问题以更准确地反映我的用例。
1赞 Remy Lebeau 2/8/2022 #2

您没有在外部 lambda 中捕获,因此它在内部 lambda 中不可用。str

如果允许你单独传递可调用对象及其参数(如 的构造函数允许),那么你可以做这样的事情:invoke()std::thread

template<typename Func>
void exceptionCatcher(Func func)
{
    try {
        func(success);
    }
    catch (const exception& e) {
        cout << "Caught exception\n";
    }
}

auto func = [str](bool success) {
    cout << str << ": " << success << endl;
    throw exception();
};

context->invoke(
    exceptionCatcher<decltype(func)>,
    func
);

或者:

void exceptionCatcher(std::function<void(bool)> func)
{
    try {
        func(success);
    }
    catch (const exception& e) {
        cout << "Caught exception\n";
    }
}

context->invoke(
    exceptionCatcher,
    [str](bool success){
        cout << str << ": " << success << endl;
        throw exception();
    }
);

否则,如果这不是一个选项,您只需要向外部 lambda 添加一个额外的捕获:

context->invoke(
    [str]{ // <-- add this
        exceptionCatcher(
            [str](bool success) {
                cout << str << ": " << success << endl;
                throw exception();
            }
        );
    }
);

评论

0赞 Edward Falk 2/8/2022
好的,谢谢,我以为这就是我要去的地方。我玩弄了使用 ,但我知道使用它有很大的开销。std::function()