【问题标题】:Wrapper to std::async() not workingstd::async() 的包装器不起作用
【发布时间】:2016-12-14 00:50:57
【问题描述】:

编辑:对 std::bind() 的调用可以替换为其他内容,我只希望 runAsyncTerminateOnException() 使用与 std::async() 相同的签名,就像它的包装器一样

我正在尝试为 std::async() 创建一个包装器。 当直接调用 std::async() 有效时,您知道如何使包装器正常工作吗?

注意:我不会修改 print() 函数签名,这是一个示例。我希望包装器是通用的,并适用于通过直接调用 std::async() 处理的所有可能参数。

谢谢。

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

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    您需要按如下方式更改您的runAsyncTerminateOnException 呼叫:

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

    这是由于an unfortunate interaction between std::bind, variadic templates and perfect forwarding

    我建议您改用 lambda,它几乎总是优于 std::bind(有关更多信息,请参阅来自 STL 的 this talk。)

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

    (请注意,我将 fn 复制到 lambda 中 - 如果您想要更正确和通用的解决方案,您应该考虑 perfect-forward capturing the object into the lambda。)

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

    wandbox example

    【讨论】:

    • 这里的 runAsyncTerminateOnException() 函数与 std::async(std::launch::async, ?) 的签名不同。你知道如何用相同的签名对 std::async() 进行包装,以及 std::async() 如何解决这个问题吗?谢谢
    • @Vittorio Romeo 您认为回调是可复制的,但可能不是。为什么不尝试将其完美转发到 lambda 中?
    • @DavidHaim:我最近wrote an article about perfectly-forwarding stuff into lambdas。它需要一些超出此问题范围的样板/知识,但我会添加一个注释。
    【解决方案2】:

    我找到了 c++17 的解决方案。 它只有在我们不使用 auto 作为 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)...);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-17
      • 1970-01-01
      相关资源
      最近更新 更多