0

EDIT : The call to std::bind() can be replaced with something else, I just want runAsyncTerminateOnException() to work with the same signature than std::async(), like just a wrapper to it

I am trying to create a wrapper to std::async(). Do you know how to make the wrapper working as well when a direct call to std::async() works ?

Note : I will not modify the print() function signature, this is an example. I would like the wrapper to be generic and to work for every possible parameters that are well handled by a direct call to std::async().

Thank you.

http://ideone.com/HbBqeo

#include <iostream>
#include <functional>
#include <future>

template<class Fn, class... Args>
inline auto runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
    auto make_call = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...);

    return std::async(std::launch::async, [=]() -> decltype(make_call()) {
        try {
            return make_call();
        } catch (...) {
            std::cout << "Terminate Called!" << std::endl;
            std::terminate();
        }
    });
}

struct Foo {
    template<class... Args>
    void print(Args&&... args) {
        printf("Foo::print(%d)\n", std::forward<Args>(args)...);
    }
};

int main() {
    Foo foo;
    std::future<void> future = std::async(std::launch::async, &Foo::print<int>, &foo, 2);
    std::future<void> future2 = runAsyncTerminateOnException(&Foo::print<int>, &foo, 2);
    // your code goes here
    return 0;
}
infiniteLoop
  • 383
  • 2
  • 12

2 Answers2

2

You need to change your runAsyncTerminateOnException call as follows:

std::future<void> future2 = 
    runAsyncTerminateOnException(&Foo::print<const int&>, &foo, 2);

This is due to an unfortunate interaction between std::bind, variadic templates and perfect forwarding.

I suggest you to use lambdas instead, which are almost always superior to std::bind. (For more information, see this talk from STL.)

template<class Fn>
inline auto runAsyncTerminateOnException(Fn&& fn) 
{    
    return std::async(std::launch::async, [=]() -> decltype(fn()) {
        try {
            return fn();
        } catch (...) {
            std::cout << "Terminate Called!" << std::endl;
            std::terminate();
        }
    });
}

(Note that I'm copying fn into the lambda - if you want a more correct and generic solution, you should consider perfect-forward capturing the object into the lambda.)

std::future<void> future2 = 
    runAsyncTerminateOnException([&foo]{ return foo.print(2); });

wandbox example

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Here the runAsyncTerminateOnException() function does not have the same signature as std::async(std::launch::async, ?). Do you know how to make a wrapper to std::async() with the same signature, and how std::async() gets over this issue ? Thank you – infiniteLoop Dec 13 '16 at 15:47
  • @Vittorio Romeo you assume the callback is copyable, which may not. why not try to perfect forward it into the lambda? – David Haim Dec 13 '16 at 15:50
  • @DavidHaim: I very recently [wrote an article about perfectly-forwarding stuff into lambdas](https://vittorioromeo.info/index/blog/capturing_perfectly_forwarded_objects_in_lambdas.html). It requires some boilerplate/knowledge that is out of the scope of this question, but I'll add a note. – Vittorio Romeo Dec 13 '16 at 16:09
0

I found the solution for c++17. It works only if we do not use auto for the return type of runTerminateOnException().

template<class Fn, class... Args>
inline std::result_of_t<Fn&&(Args&&...)> runTerminateOnException(Fn&& fn, Args&&... args) {
    try {
        return std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
    } catch (...) {
        std::terminate();
    }
}

template<class Fn, class... Args>
inline auto runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
    return std::async(std::launch::async, runTerminateOnException<Fn, Args&&...>, std::forward<Fn>(fn), std::forward<Args>(args)...);
}
infiniteLoop
  • 383
  • 2
  • 12