tl;dr:
- Working version: https://godbolt.org/z/fsxEeGf6W
- Broken version (why?): https://godbolt.org/z/KoooxsqoW
I am writing a utility function that wraps std::async
while catching all exceptions.
#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
Instead of that double-forwarding mutable lambda, I thought I could save a few lines and just use a function pointer using async's parameter-passing signature:
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)...);
;
}
This doesn't compile, for these reasons:
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)
]
What am I doing wrong? I've tried:
- Adding
std::decay_t<>
to ainvoke_result_t<...>
's parameters - Explicitly specifying the template args for
RunWithLogging
&detail::RunWithLogging
ordetail::RunWithLogging
- Using
auto
return types