简体   繁体   中英

C++ std::async() terminate on exception before calling future.get()

I am trying to create a wrapper that calls std::terminate() when it catch an exception. I would like this wrapper to take the same arguments as std::async() (it could be a call to a function as well as a call to a method). Someone know how to make this code to compile ?

Thank you

http://ideone.com/tL7mTv

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

template<class Fn, class... Args>
inline auto runTerminateOnException(Fn&& fn, Args&&... args) {
    try {
        return std::bind(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)...);
}

struct Foo {
    void print() {
        printf("Foo::print()\n");
    }
};

int main() {
    Foo foo;
    std::future<void> future = runAsyncTerminateOnException(&Foo::print, &foo);
    // your code goes here
    return 0;
}

I personally think you over complicated it a bit. You can just bind the call and use a simple lambda to do the wrapping.

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

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 {
    void print() {
        printf("Foo::print()\n");
    }
    void print2() {
        printf("Foo::print2()\n");
        throw 1;
    }
};

int main() {
    Foo foo;
    std::future<void> future = runAsyncTerminateOnException(&Foo::print, &foo);
    std::future<void> future2 = runAsyncTerminateOnException(&Foo::print2, &foo);
    return 0;
}

See it live, with possible output .


I obviously copied the first closure instead of preforming the required step to move it into the second closure (as one would do in c++11). You can of course move it with a specific move capture in c++14.

In c++17, the clean way to do this will be to use std::invoke .

I have hacked it in here to demonstrate.

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

namespace std
{
template<class T>
static constexpr bool is_member_pointer_v = std::is_member_pointer<T>::value;
template<class T>
static constexpr bool is_function_v = std::is_function<T>::value;
template<class B, class T>
static constexpr bool is_base_of_v = std::is_base_of<B, T>::value;
namespace detail {
template <class T>
struct is_reference_wrapper : std::false_type {};
template <class U>
struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {};
template <class T>
constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value;

template <class Base, class T, class Derived, class... Args>
auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args)
    noexcept(noexcept((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)))
 -> std::enable_if_t<std::is_function_v<T> &&
                     std::is_base_of_v<Base, std::decay_t<Derived>>,
    decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...))>
{
      return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...);
}

template <class Base, class T, class RefWrap, class... Args>
auto INVOKE(T Base::*pmf, RefWrap&& ref, Args&&... args)
    noexcept(noexcept((ref.get().*pmf)(std::forward<Args>(args)...)))
 -> std::enable_if_t<std::is_function_v<T> &&
                     is_reference_wrapper_v<std::decay_t<RefWrap>>,
    decltype((ref.get().*pmf)(std::forward<Args>(args)...))>

{
      return (ref.get().*pmf)(std::forward<Args>(args)...);
}

template <class Base, class T, class Pointer, class... Args>
auto INVOKE(T Base::*pmf, Pointer&& ptr, Args&&... args)
    noexcept(noexcept(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)))
 -> std::enable_if_t<std::is_function_v<T> &&
                     !is_reference_wrapper_v<std::decay_t<Pointer>> &&
                     !std::is_base_of_v<Base, std::decay_t<Pointer>>,
    decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...))>
{
      return ((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...);
}

template <class Base, class T, class Derived>
auto INVOKE(T Base::*pmd, Derived&& ref)
    noexcept(noexcept(std::forward<Derived>(ref).*pmd))
 -> std::enable_if_t<!std::is_function_v<T> &&
                     std::is_base_of_v<Base, std::decay_t<Derived>>,
    decltype(std::forward<Derived>(ref).*pmd)>
{
      return std::forward<Derived>(ref).*pmd;
}

template <class Base, class T, class RefWrap>
auto INVOKE(T Base::*pmd, RefWrap&& ref)
    noexcept(noexcept(ref.get().*pmd))
 -> std::enable_if_t<!std::is_function_v<T> &&
                     is_reference_wrapper_v<std::decay_t<RefWrap>>,
    decltype(ref.get().*pmd)>
{
      return ref.get().*pmd;
}

template <class Base, class T, class Pointer>
auto INVOKE(T Base::*pmd, Pointer&& ptr)
    noexcept(noexcept((*std::forward<Pointer>(ptr)).*pmd))
 -> std::enable_if_t<!std::is_function_v<T> &&
                     !is_reference_wrapper_v<std::decay_t<Pointer>> &&
                     !std::is_base_of_v<Base, std::decay_t<Pointer>>,
    decltype((*std::forward<Pointer>(ptr)).*pmd)>
{
      return (*std::forward<Pointer>(ptr)).*pmd;
}

template <class F, class... Args>
auto INVOKE(F&& f, Args&&... args)
    noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
 -> std::enable_if_t<!std::is_member_pointer_v<std::decay_t<F>>,
    decltype(std::forward<F>(f)(std::forward<Args>(args)...))>
{
      return std::forward<F>(f)(std::forward<Args>(args)...);
}
} // namespace detail

template< class F, class... ArgTypes >
auto invoke(F&& f, ArgTypes&&... args)
    // exception specification for QoI
    noexcept(noexcept(detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...)))
 -> decltype(detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...))
{
    return detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
}

template<class Fn, class... Args>
inline auto runAsyncTerminateOnException(Fn&& fn, Args&&... args) {

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

struct Foo {
    void print() {
        printf("Foo::print()\n");
    }
    void print2() {
        printf("Foo::print2()\n");
        throw 1;
    }
};

int main() {
    Foo foo;
    std::future<void> future = runAsyncTerminateOnException(&Foo::print, &foo);
    std::future<void> future2 = runAsyncTerminateOnException(&Foo::print2, &foo);
    return 0;
}

error when invoking templated member function:

The error is this <source>: In instantiation of 'runAsyncTerminateOnException(Fn&&, Args&& ...)::<lambda()> [with Fn = void (Foo::*)(int&&); Args = {Foo*, int}]': <source>: In instantiation of 'runAsyncTerminateOnException(Fn&&, Args&& ...)::<lambda()> [with Fn = void (Foo::*)(int&&); Args = {Foo*, int}]':

implying that Foo::print is demanding an int&& of course it is. That's what you wrote:

void print(Args&&... args)

It's not reasonable for a print function to demand ownership of objects. Declare it as it should be:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM