Home > Enterprise >  How can I wrap a closure in a closure?
How can I wrap a closure in a closure?

Time:02-08

This is my particular use case, but I'm sure there are others:

I pass a callback closure to a function that may run in a different thread. The code in the closure may throw an exception. I don't want that exception to kill my program. I'm happy to catch it and report it. (Yes, I know that doing this can result in resource leaks; I'm ignoring this issue for now.)

There are hundreds of these closures in my code, so I don't want to edit every single one of them to add try…catch clauses to them. I'd prefer to have a short wrapper around the closures instead.

My sample code looks like this:

#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;
}

and then my exceptionCatcher template looks something like this:

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

except that the compiler doesn't like this.

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(

What's the magic incantation to write exceptionCatcher?

Complication: There are many such "invoke" functions. The captured values and the arguments to the closure are varied. Is it possible to write a wrapper that handles all these cases?

CodePudding user response:

You lambda won't be a pointer. It is always passed by value.

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

CodePudding user response:

You didn't capture str in the outer lambda, so it is not available in the inner lambda.

If invoke() allows you to pass the callable object and its parameters separately (like std::thread's constructor allows), then you can do something like this:

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
);

Alternatively:

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();
    }
);

Otherwise, if that is not an option, you will just have to add an additional capture to your outer lambda:

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