diff --git a/examples/example-asio/example-asio.cpp b/examples/example-asio/example-asio.cpp index 0c8de64..83bfd3c 100644 --- a/examples/example-asio/example-asio.cpp +++ b/examples/example-asio/example-asio.cpp @@ -34,12 +34,30 @@ #include #include +#if defined(CONTINUABLE_HAS_EXCEPTIONS) +#include +#endif + #include #include using namespace std::chrono_literals; +inline auto error_code_remapper() { + return [](auto&& promise, asio::error_code e, auto&&... args) { + if (e) { +#if defined(CONTINUABLE_HAS_EXCEPTIONS) + promise.set_exception(std::make_exception_ptr(e)); +#else + promise.set_exception(cti::error_type(e.value(), e.category())); +#endif + } else { + promise.set_value(std::forward(args)...); + } + }; +} + struct functional_io_service { asio::io_context service_; asio::ip::udp::resolver resolver_; @@ -61,7 +79,8 @@ struct functional_io_service { } auto async_resolve(std::string host, std::string service) { - return cti::promisify::from( + return cti::promisify::with( + error_code_remapper(), [&](auto&&... args) { resolver_.async_resolve(std::forward(args)...); }, diff --git a/include/continuable/continuable-promisify.hpp b/include/continuable/continuable-promisify.hpp index b6bb1e3..e52cb26 100644 --- a/include/continuable/continuable-promisify.hpp +++ b/include/continuable/continuable-promisify.hpp @@ -72,18 +72,47 @@ public: /// } /// ``` /// - /// If the error code which is passed as first parameter is set there are - /// two behaviours depending whether exceptions are enabled: - /// - If exceptions are enabled the error type is passed via - /// an exception_ptr to the failure handler. - /// - If exceptions are disabled the error type is converted to a - /// `std::error_conditon` and passed down to the error handler. + /// A given error variable is converted to the used error type. + /// If this isn't possible you need to create a custom resolver callable + /// object \see with for details. /// /// \since 3.0.0 template static auto from(Callable&& callable, Args&&... args) { - return helper::template from( - std::forward(callable), std::forward(args)...); + return helper::template from(detail::convert::default_resolver(), + std::forward(callable), + std::forward(args)...); + } + + /// \copybrief from + /// + /// This modification of \ref from additionally takes a resolver callable + /// object which is used to resolve the promise from the given result. + /// + /// See an example of how to promisify boost asio's async_resolve below: + /// ```cpp + /// auto async_resolve(std::string host, std::string service) { + /// return cti::promisify::with( + /// [](auto&& promise, auto&& e, auto&&... args) { + /// if (e) { + /// promise.set_exception(std::forward(e)); + /// } else { + /// promise.set_value(std::forward(args)...); + /// } + /// }, + /// [&](auto&&... args) { + /// resolver_.async_resolve(std::forward(args)...); + /// }, + /// std::move(host), std::move(service)); + /// } + /// ``` + /// + /// \since 3.1.0 + template + static auto with(Resolver&& resolver, Callable&& callable, Args&&... args) { + return helper::template from(std::forward(resolver), + std::forward(callable), + std::forward(args)...); } }; /// \} diff --git a/include/continuable/detail/promisify.hpp b/include/continuable/detail/promisify.hpp index 10be92f..960d66e 100644 --- a/include/continuable/detail/promisify.hpp +++ b/include/continuable/detail/promisify.hpp @@ -44,46 +44,48 @@ namespace cti { namespace detail { namespace convert { -/// A helper class for promisifying asio and js style callback -/// taking functions into a continuable. -template -struct promisify_default { - P promise; - - template - void operator()(E&& error, T&&... result) { - if (error) { -#if defined(CONTINUABLE_HAS_EXCEPTIONS) - promise.set_exception(std::make_exception_ptr(std::forward(error))); -#else - promise.set_exception( - std::error_condition(error.value(), error.category())); -#endif // CONTINUABLE_HAS_EXCEPTIONS +/// A resolver for promisifying asio and js style callbacks. +inline auto default_resolver() { + return [](auto&& promise, auto&& e, auto&&... args) { + static_assert( + std::is_convertible, error_type>::value, + "The given error type must be convertible to the error type used! " + "Specify a custom resolver in order to apply a conversion to the " + "used error type."); + if (e) { + promise.set_exception(std::forward(e)); } else { - promise.set_value(std::forward(result)...); + promise.set_value(std::forward(args)...); } - } -}; + }; +} template struct promisify_helper { - template