First building API of promises and error handling

* Currently not functional (yet)
This commit is contained in:
Denis Blank 2017-09-30 02:13:27 +02:00
parent 8e8f5e982d
commit 37c70c3365
8 changed files with 274 additions and 117 deletions

View File

@ -42,6 +42,7 @@
#include <continuable/detail/composition.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/transforms.hpp>
#include <continuable/detail/types.hpp>
#include <continuable/detail/util.hpp>
namespace cti {
@ -192,9 +193,9 @@ public:
/// ```
///
/// \since version 1.0.0
template <typename T, typename E = detail::base::this_thread_executor_tag>
template <typename T, typename E = detail::types::this_thread_executor_tag>
auto then(T&& callback,
E&& executor = detail::base::this_thread_executor_tag{}) && {
E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_continuation(std::move(*this).materialize(),
std::forward<T>(callback),
std::forward<E>(executor));
@ -262,12 +263,12 @@ public:
///
///
/// \since version 2.0.0
template <typename T, typename E = detail::base::this_thread_executor_tag>
template <typename T, typename E = detail::types::this_thread_executor_tag>
auto catching(T&& callback,
E&& executor = detail::base::this_thread_executor_tag{}) && {
/*return detail::base::chain_continuation(std::move(*this).materialize(),
E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_error_handler(std::move(*this).materialize(),
std::forward<T>(callback),
std::forward<E>(executor));*/
std::forward<E>(executor));
}
/// Invokes both continuable_base objects parallel and calls the
@ -627,33 +628,6 @@ auto when_seq(Continuables&&... continuables) {
return detail::traits::fold(detail::traits::seq_folding(),
std::forward<Continuables>(continuables)...);
}
/// Trait to retrieve a continuable_base type with a given type-erasure backend.
///
/// Every object may me used as type-erasure backend as long as the
/// requirements of a `std::function` like wrapper are satisfied.
///
/// \tparam CallbackWrapper The type which is used to erase the callback.
///
/// \tparam ContinuationWrapper The type which is used to erase the
/// continuation data.
///
/// \tparam Args The current signature of the continuable.
template <template <typename> class CallbackWrapper,
template <typename> class ContinuationWrapper, typename... Args>
struct continuable_trait {
/// The callback type which is passed to continuations
using callback = CallbackWrapper<void(Args...)>;
/// The continuation type which is used to erase the internal data.
using continuation =
ContinuationWrapper<void(CallbackWrapper<void(Args...)>)>;
/// The continuable type for the given parameters.
using continuable =
continuable_base<continuation,
detail::hints::signature_hint_tag<Args...>>;
};
} // namespace cti
#endif // CONTINUABLE_BASE_HPP_INCLUDED__

View File

@ -35,14 +35,14 @@
#include <utility>
#include <continuable/detail/api.hpp>
#include <continuable/detail/base.hpp>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/types.hpp>
namespace cti {
template <typename Hint, typename Data>
template <typename Data, typename Hint>
class promise_base;
template <typename... Args, typename Data>
class promise_base<detail::hints::signature_hint_tag<Args...>, Data> {
template <typename Data, typename... Args>
class promise_base<Data, detail::hints::signature_hint_tag<Args...>> {
/// \cond false
// The callback type
Data data_;
@ -78,8 +78,8 @@ public:
}
/// Resolves the continuation with the given error variable.
void set_error(detail::base::error_type error) {
data_(detail::base::dispatch_error_tag{}, std::move(error));
void set_error(detail::types::error_type error) {
data_(detail::types::dispatch_error_tag{}, std::move(error));
}
};
} // namespace cti

View File

@ -0,0 +1,76 @@
/**
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v2.0.0
Copyright(c) 2015 - 2017 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_TRAIT_HPP_INCLUDED__
#define CONTINUABLE_TRAIT_HPP_INCLUDED__
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/detail/api.hpp>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/types.hpp>
namespace cti {
/// Trait to retrieve a continuable_base type with a given type-erasure backend.
///
/// Every object may me used as type-erasure backend as long as the
/// requirements of a `std::function` like wrapper are satisfied.
///
/// \tparam CallbackWrapper The type which is used to erase the callback.
///
/// \tparam ContinuationWrapper The type which is used to erase the
/// continuation data.
///
/// \tparam Args The current signature of the continuable.
template <template <typename...> class CallbackWrapper,
template <typename...> class ContinuationWrapper, typename... Args>
struct continuable_trait {
/// The callback type which is passed to continuations
using callback =
CallbackWrapper<void(Args...), void(detail::types::dispatch_error_tag,
detail::types::error_type)>;
/// The continuation type which is used to erase the internal data.
using continuation = ContinuationWrapper<void(
CallbackWrapper<void(Args...), void(detail::types::dispatch_error_tag,
detail::types::error_type)>)>;
/// The promise type which is used to resolve continuations
using promise =
promise_base<callback, detail::hints::signature_hint_tag<Args...>>;
/// The continuable type for the given parameters.
using continuable =
continuable_base<continuation,
detail::hints::signature_hint_tag<Args...>>;
};
} // namespace cti
#endif // CONTINUABLE_TRAIT_HPP_INCLUDED__

View File

@ -31,10 +31,10 @@
#ifndef CONTINUABLE_HPP_INCLUDED__
#define CONTINUABLE_HPP_INCLUDED__
#include "function2/function2.hpp"
#include <function2/function2.hpp>
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/detail/api.hpp>
#include <continuable/continuable-trait.hpp>
namespace cti {
// clang-format off
@ -67,19 +67,20 @@ using continuable = typename detail::trait_of<
/// function2 backend for the continuable type erasure.
///
/// Usable like: `callback<int, float>`
template <typename... Args>
using callback = typename detail::trait_of<
Args...
>::callback;
// TODO Decide whether promises are copyable
// template <typename... Args>
// using promise = typename detail::trait_of<
// Args...
// >::promise;
/// Defines a non-copyable continuation type which uses the
/// Defines a non-copyable promise type which using the
/// function2 backend for type erasure.
///
/// Usable like: `unique_continuable<int, float>`
/// Usable like: `promise<int, float>`
template <typename... Args>
using unique_continuable = typename detail::unique_trait_of<
using promise = typename detail::unique_trait_of<
Args...
>::continuable;
>::promise;
/// Defines a non-copyable callback type which uses the
/// function2 backend for the continuable type erasure.

View File

@ -35,15 +35,10 @@
#include <type_traits>
#include <utility>
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
#include <exception>
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
#include <error>
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
#include <continuable/detail/api.hpp>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/types.hpp>
#include <continuable/detail/util.hpp>
namespace cti {
@ -63,19 +58,6 @@ namespace detail {
/// base::finalize_continuation(base::continuation<auto> continuation)
/// -> void
namespace base {
/// A tag which is used to execute the continuation inside the current thread
struct this_thread_executor_tag {};
/// A tag which is used to continue with an error
struct dispatch_error_tag {};
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the error type when exceptions are enabled
using error_type = std::exception_ptr;
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the error type when exceptions are disabled
using error_type = std::error_category;
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
/// Returns the signature hint of the given continuable
template <typename T>
constexpr auto hint_of(traits::identity<T>) {
@ -204,7 +186,7 @@ constexpr auto invoker_of(traits::identity<T>) {
}
/// - void -> next_callback()
constexpr auto invoker_of(traits::identity<void>) {
inline auto invoker_of(traits::identity<void>) {
return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) {
std::forward<decltype(callback)>(callback)(
@ -248,7 +230,7 @@ constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
/// Invoke the callback immediately
template <typename Invoker, typename Callback, typename NextCallback,
typename... Args>
void packed_dispatch(this_thread_executor_tag, Invoker&& invoker,
void packed_dispatch(types::this_thread_executor_tag, Invoker&& invoker,
Callback&& callback, NextCallback&& next_callback,
Args&&... args) {
@ -276,7 +258,7 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker,
traits::unpack(std::move(args), [&](auto&&... captured_args) {
// Just use the packed dispatch method which dispatches the work on
// the current thread.
packed_dispatch(this_thread_executor_tag{}, std::move(invoker),
packed_dispatch(types::this_thread_executor_tag{}, std::move(invoker),
std::move(callback), std::move(next_callback),
std::forward<decltype(captured_args)>(captured_args)...);
});
@ -286,13 +268,14 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker,
std::forward<Executor>(executor)(std::move(work));
}
namespace callbacks {
template <typename Hint, typename Callback, typename Executor,
typename NextCallback>
struct result_proxy;
struct result_callback;
template <typename... Args, typename Callback, typename Executor,
typename NextCallback>
struct result_proxy<hints::signature_hint_tag<Args...>, Callback, Executor,
struct result_callback<hints::signature_hint_tag<Args...>, Callback, Executor,
NextCallback> {
Callback callback_;
Executor executor_;
@ -315,12 +298,77 @@ struct result_proxy<hints::signature_hint_tag<Args...>, Callback, Executor,
}
/// The operator which is called when an error occurred
void operator()(dispatch_error_tag tag, error_type error) {
void operator()(types::dispatch_error_tag tag, types::error_type error) {
// Forward the error to the next callback
std::move(next_callback_)(tag, std::move(error));
}
/// Resolves the continuation with the given values
void set_value(Args... args) {
std::move(next_callback_)(std::move(args)...);
}
/// Resolves the continuation with the given error variable.
void set_error(types::error_type error) {
std::move(next_callback_)(types::dispatch_error_tag{}, std::move(error));
}
};
template <typename Hint, typename Callback, typename Executor,
typename NextCallback>
struct error_callback;
template <typename... Args, typename Callback, typename Executor,
typename NextCallback>
struct error_callback<hints::signature_hint_tag<Args...>, Callback, Executor,
NextCallback> {
Callback callback_;
Executor executor_;
NextCallback next_callback_;
/// The operator which is called when the result was provided
void operator()(Args... args) {
// Forward the arguments to the next callback
std::move(next_callback_)(std::move(args)...);
}
/// The operator which is called when an error occurred
void operator()(types::dispatch_error_tag /*tag*/, types::error_type error) {
auto invoker = [] {};
// Forward the error to the error handler
packed_dispatch(std::move(executor_), std::move(invoker),
std::move(callback_), std::move(next_callback_),
std::move(error));
}
/// Resolves the continuation with the given values
void set_value(Args... args) {
std::move(next_callback_)(std::move(args)...);
}
/// Resolves the continuation with the given error variable.
void set_error(types::error_type error) {
std::move(next_callback_)(types::dispatch_error_tag{}, std::move(error));
}
};
/// Workaround for GCC bug:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
struct empty_callback {
template <typename... Args>
void operator()(Args... /*error*/) const {
}
template <typename... Args>
void set_value(Args... /*error*/) {
}
void set_error(types::error_type /*error*/) {
}
};
} // namespace callbacks
/// Returns the next hint when the callback is invoked with the given hint
template <typename T, typename... Args>
constexpr auto next_hint_of(traits::identity<T> /*callback*/,
@ -375,7 +423,8 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback,
// - Callback: [](std::string) { }
// - NextCallback: []() { }
using Hint = decltype(hint_of(traits::identity_of(continuation)));
result_proxy<Hint, std::decay_t<decltype(partial_callable)>,
callbacks::result_callback<Hint,
std::decay_t<decltype(partial_callable)>,
std::decay_t<decltype(executor)>,
std::decay_t<decltype(next_callback)>>
proxy{std::move(partial_callable), std::move(executor),
@ -390,31 +439,6 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback,
next_hint, ownership_);
}
template <typename Hint, typename Callback, typename Executor,
typename NextCallback>
struct error_proxy;
template <typename... Args, typename Callback, typename Executor,
typename NextCallback>
struct error_proxy<hints::signature_hint_tag<Args...>, Callback, Executor,
NextCallback> {
Callback callback_;
Executor executor_;
NextCallback next_callback_;
/// The operator which is called when the result was provided
void operator()(Args... args) {
// Forward the arguments to the next callback
std::move(next_callback_)(std::move(args)...);
}
/// The operator which is called when an error occurred
void operator()(dispatch_error_tag /*tag*/, error_type error) {
// Forwárd the error to the error handler
// TODO
}
};
/// Chains an error handler together with a continuation and
/// returns a continuation. The current future result of the continuation
//// stays unchanged.
@ -437,7 +461,7 @@ auto chain_error_handler(Continuation&& continuation, Callback&& callback,
continuation = std::forward<Continuation>(continuation),
callback = std::forward<Callback>(callback),
executor = std::forward<Executor>(executor)
](auto&& next_callback) mutable {
](auto&& /*next_callback*/) mutable {
// Invokes a continuation with a given callback.
// Passes the next callback to the resulting continuable or
// invokes the next callback directly if possible.
@ -448,7 +472,8 @@ auto chain_error_handler(Continuation&& continuation, Callback&& callback,
// - Callback: [](std::string) { }
// - NextCallback: []() { }
/*using Hint = decltype(hint_of(traits::identity_of(continuation)));
result_proxy<Hint, std::decay_t<decltype(partial_callable)>,
callbacks::result_callback<Hint,
std::decay_t<decltype(partial_callable)>,
std::decay_t<decltype(executor)>,
std::decay_t<decltype(next_callback)>>
proxy{std::move(partial_callable), std::move(executor),
@ -463,14 +488,6 @@ auto chain_error_handler(Continuation&& continuation, Callback&& callback,
hint, ownership_);
}
/// Workaround for GCC bug:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
struct empty_callback {
template <typename... Args>
void operator()(Args...) const {
}
};
/// Final invokes the given continuation chain:
///
/// For example given:
@ -478,7 +495,7 @@ struct empty_callback {
template <typename Continuation>
void finalize_continuation(Continuation&& continuation) {
attorney::invoke_continuation(std::forward<Continuation>(continuation),
empty_callback{});
callbacks::empty_callback{});
}
/// Workaround for GCC bug:

View File

@ -0,0 +1,63 @@
/**
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v2.0.0
Copyright(c) 2015 - 2017 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_DETAIL_TYPES_HPP_INCLUDED__
#define CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED__
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
#include <exception>
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
#include <error>
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
#include <continuable/detail/api.hpp>
#include <continuable/detail/features.hpp>
namespace cti {
namespace detail {
/// Contains types used globally across the library
namespace types {
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the error type when exceptions are enabled
using error_type = std::exception_ptr;
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the error type when exceptions are disabled
using error_type = std::error_condition;
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
/// A tag which is used to execute the continuation inside the current thread
struct this_thread_executor_tag {};
/// A tag which is used to continue with an error
struct dispatch_error_tag {};
} // namespace types
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED__

View File

@ -1,6 +1,7 @@
set(LIB_SOURCES
${CMAKE_SOURCE_DIR}/include/continuable/continuable.hpp
${CMAKE_SOURCE_DIR}/include/continuable/continuable-base.hpp
${CMAKE_SOURCE_DIR}/include/continuable/continuable-trait.hpp
${CMAKE_SOURCE_DIR}/include/continuable/continuable-promise-base.hpp
${CMAKE_SOURCE_DIR}/include/continuable/continuable-testing.hpp)
set(LIB_SOURCES_DETAIL
@ -11,6 +12,7 @@ set(LIB_SOURCES_DETAIL
${CMAKE_SOURCE_DIR}/include/continuable/detail/features.hpp
${CMAKE_SOURCE_DIR}/include/continuable/detail/traits.hpp
${CMAKE_SOURCE_DIR}/include/continuable/detail/transforms.hpp
${CMAKE_SOURCE_DIR}/include/continuable/detail/types.hpp
${CMAKE_SOURCE_DIR}/include/continuable/detail/util.hpp)
set(TEST
${CMAKE_CURRENT_LIST_DIR}/test-playground.cpp)

View File

@ -26,11 +26,35 @@
cti::continuable<std::string> http_request(std::string url) {
return [](cti::promise<std::string> promise) {
// ...
promise.set_error(nullptr);
promise.set_value("");
promise("");
};
}
auto http_request2(std::string url) {
return cti::make_continuable<std::string>([](auto&& promise) {
promise.set_error(nullptr);
promise.set_value("");
promise("");
});
}
int main(int, char**) {
http_request("github.com")
.then([](std::string /*response*/) {
// ...
})
.catching([](std::exception_ptr /*e*/) {
// ...
});
http_request2("github.com")
.then([](std::string /*response*/) {
// ...
})
.catching([](std::exception_ptr /*e*/) {
// ...
});
return 0;
}