2016-12-13 3 views
0

EDIT: вызов std :: bind() может быть заменен чем-то другим, я просто хочу, чтобы runAsyncTerminateOnException() работала с той же сигнатурой, что и std :: async() , как только обертка к немуWrapper to 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; 
} 

ответ

0

Я нашел решение для 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)...); 
} 
2

Вы должны изменить свой runAsyncTerminateOnException вызов следующим образом:

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

Это связано с an unfortunate interaction between std::bind, variadic templates and perfect forwarding.

Я предлагаю вам вместо этого использовать лямбда, которые почти всегда превосходят std::bind. (Для получения дополнительной информации см this talk из 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(); 
     } 
    }); 
} 

(Обратите внимание, что я копирование fn в лямбда -. Если вы хотите более правильное и общее решение, следует рассмотреть perfect-forward capturing the object into the lambda)

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

wandbox example

+0

Здесь функция runAsyncTerminateOnException() не имеет такой же подписи, как std :: async (std :: launch :: async,?). Вы знаете, как сделать оболочку для std :: async() с той же сигнатурой и как std :: async() преодолевает эту проблему? Спасибо – infiniteLoop

+0

@ Vittorio Romeo вы считаете, что обратный вызов можно копировать, что не может. почему бы не попытаться усовершенствовать его в лямбда? –

+0

@DavidHaim: Я совсем недавно [написал статью о идеально-перенаправлении материала в лямбда) (https://vittorioromeo.info/index/blog/capturing_perfectly_forwarded_objects_in_lambdas.html). Это требует некоторых шаблонов/знаний, выходящих за рамки этого вопроса, но я добавлю примечание. –

Смежные вопросы