mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 08:46:44 +08:00
First work on passing an exception to the executable work
* Make work r-value callable only by default
This commit is contained in:
parent
422b6138cd
commit
e09d26f3c6
@ -35,6 +35,7 @@
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/continuable-promise-base.hpp>
|
||||
#include <continuable/continuable-work-base.hpp>
|
||||
#include <continuable/detail/other/erasure.hpp>
|
||||
|
||||
namespace cti {
|
||||
@ -82,14 +83,16 @@ using promise = promise_base<detail::erasure::callback<Args...>, //
|
||||
signature_arg_t<Args...>>;
|
||||
|
||||
/// Defines a non-copyable type erasure which is capable of carrying
|
||||
/// callable objects passed to executors.
|
||||
/// callable objects passed to executors. Additionally the outstanding work
|
||||
/// can be resolved through an exception.
|
||||
///
|
||||
/// \note You can always define your own work with a type erasure of
|
||||
/// \note You can always define your own cancelable_work with a type erasure of
|
||||
/// choice, the type erasure wrapper just needs to accept a
|
||||
/// callable object which is callable with a `void()` signature.
|
||||
/// callable object which is callable with a `void()` and
|
||||
/// `void(exception_arg_t, exception_t)` signature.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
using work = detail::erasure::work;
|
||||
using work = work_base<detail::erasure::work>;
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
|
||||
178
include/continuable/continuable-work-base.hpp
Normal file
178
include/continuable/continuable-work-base.hpp
Normal file
@ -0,0 +1,178 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.0.0
|
||||
|
||||
Copyright(c) 2015 - 2019 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_WORK_BASE_HPP_INCLUDED
|
||||
#define CONTINUABLE_WORK_BASE_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/core/annotation.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Base Base
|
||||
/// provides classes and functions to create continuable_base objects.
|
||||
/// \{
|
||||
|
||||
/// The work_base makes it possible to resolve an asynchronous
|
||||
/// continuable through it's result or through an error type.
|
||||
///
|
||||
/// Use the work type defined in `continuable/continuable_types.hpp`,
|
||||
/// in order to use this class.
|
||||
///
|
||||
/// If we want to resolve the work_base trough the call operator,
|
||||
/// and we want to resolve it through an exception, we must call it with a
|
||||
/// exception_arg_t as first and the exception as second argument.
|
||||
/// Additionally the work is resolveable only through its call
|
||||
/// operator when invoked as an r-value.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
template <typename Data>
|
||||
class work_base
|
||||
/// \cond false
|
||||
: detail::util::non_copyable
|
||||
/// \endcond
|
||||
{ // clang-format on
|
||||
|
||||
/// \cond false
|
||||
// The work type
|
||||
Data data_;
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// Constructor for constructing an empty work
|
||||
explicit work_base() = default;
|
||||
/// Constructor accepting the data object
|
||||
explicit work_base(Data data) : data_(std::move(data)) {
|
||||
}
|
||||
|
||||
/// \cond false
|
||||
work_base(work_base&&) = default;
|
||||
work_base(work_base const&) = delete;
|
||||
|
||||
work_base& operator=(work_base&&) = default;
|
||||
work_base& operator=(work_base const&) = delete;
|
||||
/// \endcond
|
||||
|
||||
/// Constructor accepting any object convertible to the data object
|
||||
template <typename OData,
|
||||
std::enable_if_t<std::is_convertible<
|
||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||
/* implicit */ work_base(OData&& data) : data_(std::forward<OData>(data)) {
|
||||
}
|
||||
|
||||
/// Assignment operator accepting any object convertible to the data object
|
||||
template <typename OData,
|
||||
std::enable_if_t<std::is_convertible<
|
||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||
work_base& operator=(OData&& data) {
|
||||
data_ = std::forward<OData>(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the given values.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the work is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the work such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in work_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid work_base is undefined!
|
||||
///
|
||||
/// \since 4.0.0
|
||||
void operator()() && noexcept {
|
||||
std::move(data_)();
|
||||
data_ = nullptr;
|
||||
}
|
||||
/// Resolves the continuation with the given exception.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the work is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the work such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in work_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid work_base is undefined!
|
||||
///
|
||||
/// \since 4.0.0
|
||||
void operator()(exception_arg_t tag, exception_t exception) && noexcept {
|
||||
std::move(data_)(tag, std::move(exception));
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the given values.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the work is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the work such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in work_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid work_base is undefined!
|
||||
///
|
||||
/// \since 4.0.0
|
||||
void set_value() noexcept {
|
||||
std::move(data_)();
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the given exception.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the work is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the work such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in work_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid work_base is undefined!
|
||||
///
|
||||
/// \since 4.0.0
|
||||
void set_exception(exception_t exception) noexcept {
|
||||
std::move(data_)(exception_arg_t{}, std::move(exception));
|
||||
data_ = nullptr;
|
||||
}
|
||||
};
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_WORK_BASE_HPP_INCLUDED
|
||||
@ -57,5 +57,6 @@ namespace cti {}
|
||||
#include <continuable/continuable-traverse-async.hpp>
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
#include <continuable/continuable-types.hpp>
|
||||
#include <continuable/continuable-work-base.hpp>
|
||||
|
||||
#endif // CONTINUABLE_HPP_INCLUDED
|
||||
|
||||
@ -472,36 +472,76 @@ constexpr auto invoker_of(identity<std::tuple<Args...>>) {
|
||||
} // namespace decoration
|
||||
|
||||
/// Invoke the callback immediately
|
||||
template <typename Invoker, typename... Args>
|
||||
template <typename Invoker, typename Callback, typename NextCallback,
|
||||
typename... Args>
|
||||
void on_executor(types::this_thread_executor_tag, Invoker&& invoker,
|
||||
Callback&& callback, NextCallback&& next_callback,
|
||||
Args&&... args) {
|
||||
|
||||
// Invoke the callback with the decorated invoker immediately
|
||||
std::forward<Invoker>(invoker)(std::forward<Args>(args)...);
|
||||
std::forward<Invoker>(invoker)(std::forward<Callback>(callback),
|
||||
std::forward<NextCallback>(next_callback),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Invoke the callback through the given executor
|
||||
template <typename Executor, typename Invoker, typename... Args>
|
||||
void on_executor(Executor&& executor, Invoker&& invoker, Args&&... args) {
|
||||
template <typename Invoker, typename Callback, typename NextCallback,
|
||||
typename... Args>
|
||||
class work_proxy {
|
||||
public:
|
||||
work_proxy(Invoker&& invoker, Callback&& callback,
|
||||
NextCallback&& next_callback, std::tuple<Args...>&& args)
|
||||
: invoker_(std::move(invoker)), callback_(std::move(callback)),
|
||||
next_callback_(std::move(next_callback)), args_(std::move(args)) {
|
||||
}
|
||||
~work_proxy() = default;
|
||||
work_proxy(work_proxy&&) = default;
|
||||
work_proxy(work_proxy const&) = delete;
|
||||
work_proxy& operator=(work_proxy&&) = default;
|
||||
work_proxy& operator=(work_proxy const&) = delete;
|
||||
|
||||
// Create a worker object which when invoked calls the callback with the
|
||||
// the returned arguments.
|
||||
auto work = [
|
||||
invoker = std::forward<Invoker>(invoker),
|
||||
args = std::make_tuple(std::forward<Args>(args)...)
|
||||
]() mutable {
|
||||
void operator()() && {
|
||||
traits::unpack(
|
||||
[&](auto&&... captured_args) {
|
||||
// Just use the packed dispatch method which dispatches the work on
|
||||
// the current thread.
|
||||
on_executor(types::this_thread_executor_tag{}, std::move(invoker),
|
||||
std::forward<decltype(captured_args)>(captured_args)...);
|
||||
// Just use the packed dispatch method which dispatches the work_proxy
|
||||
// on the current thread.
|
||||
std::move(invoker_)(
|
||||
std::move(callback_), std::move(next_callback_),
|
||||
std::forward<decltype(captured_args)>(captured_args)...);
|
||||
},
|
||||
std::move(args));
|
||||
};
|
||||
std::move(args_));
|
||||
}
|
||||
|
||||
// Pass the work callable object to the executor
|
||||
std::forward<Executor>(executor)(std::move(work));
|
||||
void operator()(exception_arg_t, exception_t exception) && {
|
||||
std::move(next_callback_)(exception_arg_t{}, std::move(exception));
|
||||
}
|
||||
|
||||
void set_exception(exception_t exception) noexcept {
|
||||
std::move(next_callback_)(exception_arg_t{}, std::move(exception));
|
||||
}
|
||||
|
||||
private:
|
||||
Invoker invoker_;
|
||||
Callback callback_;
|
||||
NextCallback next_callback_;
|
||||
std::tuple<Args...> args_;
|
||||
};
|
||||
|
||||
/// Invoke the callback through the given executor
|
||||
template <typename Executor, typename Invoker, typename Callback,
|
||||
typename NextCallback, typename... Args>
|
||||
void on_executor(Executor&& executor, Invoker&& invoker, Callback&& callback,
|
||||
NextCallback&& next_callback, Args&&... args) {
|
||||
|
||||
// Create a work_proxy object which when invoked calls the callback with the
|
||||
// the returned arguments and pass the work_proxy callable object to the
|
||||
// executor
|
||||
using work_proxy_t =
|
||||
work_proxy<Invoker, std::decay_t<Callback>, std::decay_t<NextCallback>,
|
||||
std::decay_t<Args>...>;
|
||||
std::forward<Executor>(executor)(work_proxy_t(
|
||||
std::forward<Invoker>(invoker), std::forward<Callback>(callback),
|
||||
std::forward<NextCallback>(next_callback),
|
||||
std::make_tuple(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
/// Tells whether we potentially move the chain upwards and handle the result
|
||||
|
||||
@ -178,12 +178,24 @@ public:
|
||||
};
|
||||
#endif
|
||||
|
||||
using work_erasure_t = fu2::unique_function<void()>;
|
||||
using work_erasure_t =
|
||||
fu2::function_base<true, false, fu2::capacity_fixed<32UL>, true, false,
|
||||
void()&&, void(exception_arg_t, exception_t) &&>;
|
||||
|
||||
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||
using work = work_erasure_t;
|
||||
#else
|
||||
class work : public work_erasure_t {
|
||||
class work;
|
||||
|
||||
template <typename T>
|
||||
struct is_work : std::false_type {};
|
||||
template <>
|
||||
struct is_work<work> : std::true_type {};
|
||||
|
||||
class work {
|
||||
using erasure_t = work_erasure_t;
|
||||
erasure_t erasure_;
|
||||
|
||||
public:
|
||||
work() = default;
|
||||
~work() = default;
|
||||
@ -192,11 +204,31 @@ public:
|
||||
work& operator=(work const&) = delete;
|
||||
work& operator=(work&&) = default;
|
||||
|
||||
using work_erasure_t::work_erasure_t;
|
||||
using work_erasure_t::operator=;
|
||||
using work_erasure_t::operator();
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_convertible<T, erasure_t>::value>* = nullptr,
|
||||
std::enable_if_t<!is_work<traits::unrefcv_t<T>>::value>* = nullptr>
|
||||
/* implicit */ work(T&& callable) : erasure_(std::forward<T>(callable)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_assignable<erasure_t, T>::value>* = nullptr,
|
||||
std::enable_if_t<!is_work<traits::unrefcv_t<T>>::value>* = nullptr>
|
||||
work& operator=(T&& callable) {
|
||||
erasure_ = std::forward<T>(callable);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()() && {
|
||||
std::move(erasure_)();
|
||||
}
|
||||
|
||||
void operator()(exception_arg_t, exception_t e) && {
|
||||
std::move(erasure_)(exception_arg_t{}, std::move(e));
|
||||
}
|
||||
};
|
||||
#endif // CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||
#endif
|
||||
} // namespace erasure
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
@ -61,7 +61,7 @@ namespace cti {
|
||||
/// \param args The arguments which are passed to the callable upon invocation.
|
||||
///
|
||||
/// \returns A continuable_base which asynchronous result type will
|
||||
/// be computated with the same rules as continuable_base::then .
|
||||
/// be computed with the same rules as continuable_base::then .
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
@ -100,7 +100,7 @@ auto async(Callable&& callable, Args&&... args) {
|
||||
/// \param args The arguments which are passed to the callable upon invocation.
|
||||
///
|
||||
/// \returns A continuable_base which asynchronous result type will
|
||||
/// be computated with the same rules as continuable_base::then .
|
||||
/// be computed with the same rules as continuable_base::then .
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
|
||||
@ -26,4 +26,21 @@ using namespace cti;
|
||||
|
||||
int main(int, char**) {
|
||||
// ...
|
||||
auto e = std::make_exception_ptr(std::exception("huhu"));
|
||||
|
||||
async_on(
|
||||
[] {
|
||||
//
|
||||
int i = 0;
|
||||
},
|
||||
[&](work work) {
|
||||
int i = 0;
|
||||
(void)i;
|
||||
work.set_exception(e); //
|
||||
})
|
||||
.fail([](exception_t e) {
|
||||
//
|
||||
int i = 0;
|
||||
(void)i;
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user