mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Make it possible to remap the result from promisified expressions
This commit is contained in:
parent
b68cd1b43a
commit
7dbf22a2d2
@ -34,12 +34,30 @@
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#include <exception>
|
||||
#endif
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <continuable/continuable.hpp>
|
||||
|
||||
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<decltype(args)>(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<asio::ip::udp::resolver::iterator>::from(
|
||||
return cti::promisify<asio::ip::udp::resolver::iterator>::with(
|
||||
error_code_remapper(),
|
||||
[&](auto&&... args) {
|
||||
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
|
||||
@ -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 <typename Callable, typename... Args>
|
||||
static auto from(Callable&& callable, Args&&... args) {
|
||||
return helper::template from<detail::convert::promisify_default>(
|
||||
std::forward<Callable>(callable), std::forward<Args>(args)...);
|
||||
return helper::template from(detail::convert::default_resolver(),
|
||||
std::forward<Callable>(callable),
|
||||
std::forward<Args>(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<asio::ip::udp::resolver::iterator>::with(
|
||||
/// [](auto&& promise, auto&& e, auto&&... args) {
|
||||
/// if (e) {
|
||||
/// promise.set_exception(std::forward<decltype(e)>(e));
|
||||
/// } else {
|
||||
/// promise.set_value(std::forward<decltype(args)>(args)...);
|
||||
/// }
|
||||
/// },
|
||||
/// [&](auto&&... args) {
|
||||
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
/// },
|
||||
/// std::move(host), std::move(service));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// \since 3.1.0
|
||||
template <typename Resolver, typename Callable, typename... Args>
|
||||
static auto with(Resolver&& resolver, Callable&& callable, Args&&... args) {
|
||||
return helper::template from(std::forward<Resolver>(resolver),
|
||||
std::forward<Callable>(callable),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
/// \}
|
||||
|
||||
@ -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 <typename P>
|
||||
struct promisify_default {
|
||||
P promise;
|
||||
|
||||
template <typename E, typename... T>
|
||||
void operator()(E&& error, T&&... result) {
|
||||
if (error) {
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
promise.set_exception(std::make_exception_ptr(std::forward<E>(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<std::decay_t<decltype(e)>, 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<decltype(e)>(e));
|
||||
} else {
|
||||
promise.set_value(std::forward<T>(result)...);
|
||||
promise.set_value(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... Result>
|
||||
struct promisify_helper {
|
||||
template <template <class T> class Evaluator, typename Callable,
|
||||
typename... Args>
|
||||
static auto from(Callable&& callable, Args&&... args) {
|
||||
return make_continuable<Result...>([args = std::make_tuple(
|
||||
std::forward<Callable>(callable),
|
||||
std::forward<Args>(args)...)](
|
||||
auto&& promise) mutable {
|
||||
template <typename Resolver, typename Callable, typename... Args>
|
||||
static auto from(Resolver&& resolver, Callable&& callable, Args&&... args) {
|
||||
return make_continuable<Result...>([
|
||||
resolver = std::forward<Resolver>(resolver),
|
||||
args = traits::make_flat_tuple(std::forward<Callable>(callable),
|
||||
std::forward<Args>(args)...)
|
||||
](auto&& promise) mutable {
|
||||
|
||||
traits::unpack(
|
||||
[promise = std::forward<decltype(promise)>(promise)](
|
||||
auto&&... args) mutable {
|
||||
Evaluator<std::decay_t<decltype(promise)>> evaluator{
|
||||
std::move(promise)};
|
||||
[ promise = std::forward<decltype(promise)>(promise),
|
||||
&resolver ](auto&&... args) mutable {
|
||||
|
||||
// Call the resolver from with the promise and result
|
||||
auto callback = [
|
||||
resolver = std::move(resolver), promise = std::move(promise)
|
||||
](auto&&... args) mutable {
|
||||
resolver(std::move(promise),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
|
||||
// Invoke the callback taking function
|
||||
util::invoke(std::forward<decltype(args)>(args)...,
|
||||
std::move(evaluator));
|
||||
std::move(callback));
|
||||
},
|
||||
std::move(args));
|
||||
});
|
||||
|
||||
@ -57,6 +57,13 @@ struct index_of_impl<T, U, Args...>
|
||||
template <typename T, typename... Args>
|
||||
using index_of_t = detail::index_of_impl<T, Args...>;
|
||||
|
||||
/// Creates a tuple in which r-values gets copied and
|
||||
/// l-values keep their l-value.
|
||||
template <typename... T>
|
||||
auto make_flat_tuple(T&&... args) {
|
||||
return std::tuple<T...>{std::forward<T>(args)...};
|
||||
}
|
||||
|
||||
/// A tagging type for wrapping other types
|
||||
template <typename... T>
|
||||
struct identity {};
|
||||
|
||||
@ -18,6 +18,7 @@ add_executable(test-continuable-single
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-connection-noinst
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-flat-variant.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-expected.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-promisify.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-traverse.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-traverse-async.cpp)
|
||||
|
||||
|
||||
68
test/unit-test/single/test-continuable-promisify.cpp
Normal file
68
test/unit-test/single/test-continuable-promisify.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
/*
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
**/
|
||||
|
||||
#include <test-continuable.hpp>
|
||||
|
||||
template <typename T, typename Callback>
|
||||
void async_supply(T&& value, Callback&& callback) {
|
||||
std::forward<Callback>(callback)(cti::error_type{}, std::forward<T>(value));
|
||||
}
|
||||
|
||||
TEST(promisify_tests, promisify_from) {
|
||||
|
||||
auto c = cti::promisify<int>::from(
|
||||
[&](auto&&... args) {
|
||||
async_supply(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
36354);
|
||||
|
||||
EXPECT_ASYNC_RESULT(std::move(c), 36354);
|
||||
}
|
||||
|
||||
TEST(promisify_tests, promisify_from_lvalue) {
|
||||
int value = 0;
|
||||
auto c = cti::promisify<int>::from(
|
||||
[&](auto&&... args) {
|
||||
async_supply(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
value);
|
||||
|
||||
value = 36354;
|
||||
EXPECT_ASYNC_RESULT(std::move(c), 36354);
|
||||
}
|
||||
|
||||
TEST(promisify_tests, promisify_with) {
|
||||
int value = 0;
|
||||
auto c = cti::promisify<int>::with(
|
||||
[](auto&& promise, auto&& /*e*/, int const& value) {
|
||||
EXPECT_EQ(value, 36354);
|
||||
promise.set_exception(cti::error_type{});
|
||||
},
|
||||
[&](auto&&... args) {
|
||||
async_supply(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
value);
|
||||
|
||||
value = 36354;
|
||||
ASSERT_ASYNC_EXCEPTION_COMPLETION(std::move(c));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user