mirror of
https://github.com/Naios/continuable.git
synced 2025-12-07 01:06: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-base.hpp>
|
||||||
#include <continuable/continuable-primitives.hpp>
|
#include <continuable/continuable-primitives.hpp>
|
||||||
#include <continuable/continuable-promise-base.hpp>
|
#include <continuable/continuable-promise-base.hpp>
|
||||||
|
#include <continuable/continuable-work-base.hpp>
|
||||||
#include <continuable/detail/other/erasure.hpp>
|
#include <continuable/detail/other/erasure.hpp>
|
||||||
|
|
||||||
namespace cti {
|
namespace cti {
|
||||||
@ -82,14 +83,16 @@ using promise = promise_base<detail::erasure::callback<Args...>, //
|
|||||||
signature_arg_t<Args...>>;
|
signature_arg_t<Args...>>;
|
||||||
|
|
||||||
/// Defines a non-copyable type erasure which is capable of carrying
|
/// 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
|
/// 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
|
/// \since 4.0.0
|
||||||
using work = detail::erasure::work;
|
using work = work_base<detail::erasure::work>;
|
||||||
/// \}
|
/// \}
|
||||||
} // namespace cti
|
} // 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-async.hpp>
|
||||||
#include <continuable/continuable-traverse.hpp>
|
#include <continuable/continuable-traverse.hpp>
|
||||||
#include <continuable/continuable-types.hpp>
|
#include <continuable/continuable-types.hpp>
|
||||||
|
#include <continuable/continuable-work-base.hpp>
|
||||||
|
|
||||||
#endif // CONTINUABLE_HPP_INCLUDED
|
#endif // CONTINUABLE_HPP_INCLUDED
|
||||||
|
|||||||
@ -472,36 +472,76 @@ constexpr auto invoker_of(identity<std::tuple<Args...>>) {
|
|||||||
} // namespace decoration
|
} // namespace decoration
|
||||||
|
|
||||||
/// Invoke the callback immediately
|
/// 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,
|
void on_executor(types::this_thread_executor_tag, Invoker&& invoker,
|
||||||
|
Callback&& callback, NextCallback&& next_callback,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
|
|
||||||
// Invoke the callback with the decorated invoker immediately
|
// 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 Invoker, typename Callback, typename NextCallback,
|
||||||
template <typename Executor, typename Invoker, typename... Args>
|
typename... Args>
|
||||||
void on_executor(Executor&& executor, Invoker&& invoker, Args&&... 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
|
void operator()() && {
|
||||||
// the returned arguments.
|
|
||||||
auto work = [
|
|
||||||
invoker = std::forward<Invoker>(invoker),
|
|
||||||
args = std::make_tuple(std::forward<Args>(args)...)
|
|
||||||
]() mutable {
|
|
||||||
traits::unpack(
|
traits::unpack(
|
||||||
[&](auto&&... captured_args) {
|
[&](auto&&... captured_args) {
|
||||||
// Just use the packed dispatch method which dispatches the work on
|
// Just use the packed dispatch method which dispatches the work_proxy
|
||||||
// the current thread.
|
// on the current thread.
|
||||||
on_executor(types::this_thread_executor_tag{}, std::move(invoker),
|
std::move(invoker_)(
|
||||||
|
std::move(callback_), std::move(next_callback_),
|
||||||
std::forward<decltype(captured_args)>(captured_args)...);
|
std::forward<decltype(captured_args)>(captured_args)...);
|
||||||
},
|
},
|
||||||
std::move(args));
|
std::move(args_));
|
||||||
};
|
}
|
||||||
|
|
||||||
// Pass the work callable object to the executor
|
void operator()(exception_arg_t, exception_t exception) && {
|
||||||
std::forward<Executor>(executor)(std::move(work));
|
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
|
/// Tells whether we potentially move the chain upwards and handle the result
|
||||||
|
|||||||
@ -178,12 +178,24 @@ public:
|
|||||||
};
|
};
|
||||||
#endif
|
#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
|
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||||
using work = work_erasure_t;
|
using work = work_erasure_t;
|
||||||
#else
|
#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:
|
public:
|
||||||
work() = default;
|
work() = default;
|
||||||
~work() = default;
|
~work() = default;
|
||||||
@ -192,11 +204,31 @@ public:
|
|||||||
work& operator=(work const&) = delete;
|
work& operator=(work const&) = delete;
|
||||||
work& operator=(work&&) = default;
|
work& operator=(work&&) = default;
|
||||||
|
|
||||||
using work_erasure_t::work_erasure_t;
|
template <
|
||||||
using work_erasure_t::operator=;
|
typename T,
|
||||||
using work_erasure_t::operator();
|
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 erasure
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace cti
|
} // namespace cti
|
||||||
|
|||||||
@ -61,7 +61,7 @@ namespace cti {
|
|||||||
/// \param args The arguments which are passed to the callable upon invocation.
|
/// \param args The arguments which are passed to the callable upon invocation.
|
||||||
///
|
///
|
||||||
/// \returns A continuable_base which asynchronous result type will
|
/// \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
|
/// \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.
|
/// \param args The arguments which are passed to the callable upon invocation.
|
||||||
///
|
///
|
||||||
/// \returns A continuable_base which asynchronous result type will
|
/// \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
|
/// \since 4.0.0
|
||||||
///
|
///
|
||||||
|
|||||||
@ -26,4 +26,21 @@ using namespace cti;
|
|||||||
|
|
||||||
int main(int, char**) {
|
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