c++ 更改std::async以使用函数指针而不是lambda

ohtdti5x  于 2023-01-15  发布在  其他
关注(0)|答案(1)|浏览(125)

TL;医生:

我正在编写一个实用函数,它在捕获所有异常的同时 Package std::async

#include <functional>
#include <future>
#include <iostream>
#include <string>
#include <thread>

namespace Async {

namespace detail {
template <typename F, typename... Args>
std::invoke_result_t<F, Args...> RunWithLogging(std::string label, F&& f,
                                                Args&&... args) {
  try {
    return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
  } catch (std::exception& ex) {
    std::cout << label << "Exception escaped thread \"" << label
              << "\": " << ex.what();
    throw;
  } catch (...) {
    std::cout << label << "Exception escaped thread \"" << label
              << "\": (non-standard exception type)";
    throw;
  }
}
}  // namespace detail

/// Like std::async(std::launch::async, f, args...):
/// - Catches and logs any escaped exceptions
/// - Returned future joins the thread on destructor
template <typename F, typename... Args>
[[nodiscard]] std::future<std::invoke_result_t<F, Args...>> Launch(
    std::string label, F&& f, Args&&... args) {
  return std::async(
      std::launch::async, [label = std::move(label), f = std::forward<F>(f),
                           ... args = std::forward<Args>(args)]() mutable {
        return detail::RunWithLogging(std::move(label), std::forward<F>(f),
                                      std::forward<Args>(args)...);
      });
}

}  // namespace Async

我想我可以节省几行代码,只使用一个使用async的参数传递签名的函数指针,而不用那个双转发可变lambda:

template <typename F, typename... Args>
[[nodiscard]] std::future<std::invoke_result_t<F, Args...>> Launch(
    std::string label, F&& f, Args&&... args) {
  return std::async(std::launch::async, &detail::RunWithLogging,
                    std::move(label), std::forward<F>(f),
                    std::forward<Args>(args)...);
  ;
}

由于以下原因,该代码无法编译:

C:\...\lib\Async\Thread.h(33,15): error C2672: 'async': no matching overloaded function found [C:\...\build\lib\Async\AsyncTest.vcxproj]
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\future(1535,81): message : could be 'std::future<_Select_invoke_traits<decay<_Ty>::type,decay<_ArgTypes>::type...>::type> std::async(_Fty &&,_ArgTypes &&...)' [C:\...\build\lib\Async\AsyncTest.vcxproj]
C:\...\lib\Async\Thread.h(33,15): message : 'std::future<_Select_invoke_traits<decay<_Ty>::type,decay<_ArgTypes>::type...>::type> std::async(_Fty &&,_ArgTypes &&...)': could not deduce template argument for '_ArgTypes &&' from 'overloaded-function' [C:\...\build\lib\Async\AsyncTest.vcxproj]
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\future(1522,81): message : or       'std::future<_Select_invoke_traits<decay<_Ty>::type,decay<_ArgTypes>::type...>::type> std::async(std::launch,_Fty &&,_ArgTypes &&...)' [C:\...\build\lib\Async\AsyncTest.vcxproj]
C:\...\lib\Async\Thread.h(32,47): message : 'std::future<_Select_invoke_traits<decay<_Ty>::type,decay<_ArgTypes>::type...>::type> std::async(std::launch,_Fty &&,_ArgTypes &&...)': could not deduce template argument for '_Fty' [C:\...\build\lib\Async\AsyncTest.vcxproj]
C:\...\lib\Async\ThreadTest.cpp(24,57): message : see reference to function template instantiation 'std::future<int> Async::Launch<int(__cdecl *)(int),int>(std::string,F &&,int &&)' being compiled [C:\...\build\lib\Async\AsyncTest.vcxproj]
          with
          [
              F=int (__cdecl *)(int)
          ]

我做错什么了?我试过了:

  • std::decay_t<>添加到invoke_result_t<...>的参数
  • 显式指定RunWithLogging的模板参数
  • &detail::RunWithLoggingdetail::RunWithLogging
  • 使用auto返回类型
ttisahbt

ttisahbt1#

我认为问题在于引用没有被传递给线程,并且std::async的函数参数没有被完全专用化。
我专门化了detail::RunWithLogging,并按值传递标签:

return std::async(std::launch::async, &detail::RunWithLogging<F,Args...>,
label, std::forward<F>(f),
std::forward<Args>(args)...);

然后,我将std::ref添加到lambda函数中。

auto lambda = [](int a, int b) { return a + b; };
auto f = Async::Launch("Lambda", std::ref(lambda), 1, 2);
EXPECT_EQ(f.get(), 3);

最后,我用std::ref Package 了hi,这修复了所有编译问题。

std::string hi = "hello";
auto f =
    Async::Launch("SomethingWithConstRef", &SomethingWithConstRef, std::ref(hi));
EXPECT_EQ(f.get(), "hello!");

Lambda很好,因为它们无需使用std::ref即可捕获引用。
有关详细信息,请参见this other post

相关问题