Start to revise transforms

This commit is contained in:
Denis Blank 2020-04-04 00:04:51 +02:00
parent 957d3fa375
commit df4d6ed971
10 changed files with 332 additions and 108 deletions

View File

@ -436,14 +436,15 @@ public:
return std::move(*this).then(detail::base::convert_to<Args...>{});
}
/// A method which allows to apply this continuable to the given callable.
/// A method which allows to apply a callable object to this continuable.
///
/// \param transform A transform which shall accept this continuable
/// \param transform A callable objects that transforms a continuable
/// to a different object.
///
/// \returns Returns the result of the given transform when this
/// continuable is passed into it.
///
/// \since 2.0.0
/// \since 4.0.0
template <typename T>
auto apply(T&& transform) && {
return std::forward<T>(transform)(std::move(*this).finish());
@ -462,22 +463,6 @@ public:
return std::move(*this).then(std::forward<T>(right));
}
/// The pipe operator | is an alias for the continuable::apply method.
///
/// \param transform The transformer which is applied.
///
/// \returns See the corresponding continuable_base::apply method for the
/// explanation of the return type.
///
/// \note You may create your own transformation through
/// calling make_transformation.
///
/// \since 3.0.0
template <typename T>
auto operator|(detail::types::transform<T> transform) && {
return std::move(*this).apply(std::move(transform));
}
/// Invokes both continuable_base objects parallel and calls the
/// callback with the result of both continuable_base objects.
///

View File

@ -31,8 +31,8 @@
#ifndef CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#define CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/other/transforms.hpp>
#include <continuable/transforms/wait.hpp>
#include <continuable/transforms/future.hpp>
namespace cti {
/// \defgroup Transforms Transforms
@ -41,70 +41,12 @@ namespace cti {
/// types such as (`std::future`).
/// \{
/// A callable tag object which marks a wrapped callable object
/// as continuable transformation which enables some useful overloads.
///
/// \since 3.0.0
template <typename T>
using transform = detail::types::transform<T>;
/// Wraps the given callable object into a transform class.
///
/// \since 3.0.0
template <typename T>
auto make_transform(T&& callable) {
return transform<std::decay_t<T>>(std::forward<T>(callable));
}
/// The namespace transforms declares callable objects that transform
/// any continuable_base to an object or to a continuable_base itself.
///
/// Transforms can be applied to continuables through using
/// the cti::continuable_base::apply method accordingly.
namespace transforms {
/// Returns a transform that if applied to a continuable,
/// it will start the continuation chain and returns the asynchronous
/// result as `std::future<...>`.
///
/// \returns Returns a `std::future<...>` which becomes ready as soon
/// as the the continuation chain has finished.
/// The signature of the future depends on the result type:
/// | Continuation type | Return type |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `std::future<void>` |
/// | `continuable_base with <Arg>` | `std::future<Arg>` |
/// | `continuable_base with <Args...>` | `std::future<std::tuple<Args...>>` |
///
/// \attention If exceptions are used, exceptions that are thrown, are forwarded
/// to the returned future. If there are no exceptions supported,
/// you shall not pass any errors to the end of the asynchronous
/// call chain!
/// Otherwise this will yield a trap that causes application exit.
///
/// \since 2.0.0
inline auto futurize() {
return make_transform([](auto&& continuable) {
using detail::transforms::as_future;
return as_future(std::forward<decltype(continuable)>(continuable));
});
}
/// Returns a transform that if applied to a continuable, it will ignores all
/// error which ocured until the point the transform was applied.
///
/// \returns Returns a continuable with the same signature as applied to.
///
/// \attention This can be used to create a continuable which doesn't resolve
/// the continuation on errors.
///
/// \since 2.0.0
inline auto flatten() {
return make_transform([](auto&& continuable) {
return std::forward<decltype(continuable)>(continuable).fail([](auto&&) {});
});
}
/// \}
} // namespace transforms
namespace transforms {}
} // namespace cti
#endif // CONTINUABLE_TRANSFORMS_HPP_INCLUDED

View File

@ -53,7 +53,6 @@ namespace cti {}
#include <continuable/continuable-promise-base.hpp>
#include <continuable/continuable-promisify.hpp>
#include <continuable/continuable-result.hpp>
#include <continuable/continuable-transforms.hpp>
#include <continuable/continuable-traverse-async.hpp>
#include <continuable/continuable-traverse.hpp>
#include <continuable/continuable-types.hpp>

View File

@ -66,16 +66,6 @@ using exception_t = std::error_condition;
/// A tag which is used to execute the continuation inside the current thread
struct this_thread_executor_tag {};
/// Marks a given callable object as transformation
template <typename T>
class transform : T {
public:
explicit transform(T callable) : T(std::move(callable)) {
}
using T::operator();
};
/// Marks a given callable object as transformation
template <typename T>
class plain_tag {

View File

@ -28,8 +28,8 @@
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TRANSFORMS_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRANSFORMS_HPP_INCLUDED
#ifndef CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED
#include <future>
#include <continuable/continuable-primitives.hpp>
@ -115,7 +115,7 @@ public:
/// Transforms the continuation to a future
template <typename Data, typename Annotation>
auto as_future(continuable_base<Data, Annotation>&& continuable) {
auto to_future(continuable_base<Data, Annotation>&& continuable) {
// Create the promise which is able to supply the current arguments
constexpr auto const hint =
base::annotation_of(identify<decltype(continuable)>{});
@ -135,4 +135,4 @@ auto as_future(continuable_base<Data, Annotation>&& continuable) {
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TRANSFORMS_HPP_INCLUDED
#endif // CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED

View File

@ -0,0 +1,134 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
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_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED
#include <atomic>
#include <cassert>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/util.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
# include <exception>
#endif
namespace cti {
namespace detail {
namespace transforms {
/*
template <typename... Args>
struct sync_trait {
/// The promise type used to create the future
using promise_t = std::tuple<Args...>;
/// Boxes the argument pack into a tuple
static void resolve(promise_t& promise, Args... args) {
promise.set_value(std::make_tuple(std::move(args)...));
}
};
template <>
struct sync_trait<> {
/// The promise type used to create the future
using promise_t = result<Args...>;
/// Boxes the argument pack into void
static void resolve(promise_t& promise) {
promise.set_value();
}
};
template <typename First>
struct sync_trait<First> {
/// The promise type used to create the future
using promise_t = std::promise<First>;
/// Boxes the argument pack into nothing
static void resolve(promise_t& promise, First first) {
promise.set_value(std::move(first));
}
};
*/
template <typename Hint>
struct sync_trait;
template <typename... Args>
struct sync_trait<identity<Args...>> {
using result_t = result<Args...>;
};
/// Transforms the continuation to sync execution
template <typename Data, typename Annotation>
auto wait(continuable_base<Data, Annotation>&& continuable) {
constexpr auto hint = base::annotation_of(identify<decltype(continuable)>{});
using result_t = typename sync_trait<std::decay_t<decltype(hint)>>::result_t;
(void)hint;
std::recursive_mutex mutex;
std::condition_variable_any cv;
std::atomic_bool ready{false};
result_t sync_result;
std::move(continuable)
.next([&](auto&&... args) {
sync_result = result_t::from(std::forward<decltype(args)>(args)...);
ready.store(true, std::memory_order_release);
cv.notify_all();
})
.done();
if (!ready.load(std::memory_order_acquire)) {
std::unique_lock<std::recursive_mutex> lock(mutex);
cv.wait(lock, [&] {
return ready.load(std::memory_order_acquire);
});
}
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
if (sync_result.is_value()) {
return std::move(sync_result).get_value();
} else {
assert(sync_result.is_exception());
std::rethrow_exception(sync_result.get_exception());
}
#else
return sync_result;
#endif // CONTINUABLE_HAS_EXCEPTIONS
}
} // namespace transforms
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED

View File

@ -0,0 +1,72 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
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_TRANSFORMS_FUTURE_HPP_INCLUDED
#define CONTINUABLE_TRANSFORMS_FUTURE_HPP_INCLUDED
#include <utility>
#include <continuable/detail/transforms/future.hpp>
namespace cti {
/// \ingroup Transforms
/// \{
namespace transforms {
/// Returns a transform that if applied to a continuable,
/// it will start the continuation chain and returns the asynchronous
/// result as `std::future<...>`.
///
/// \returns Returns a `std::future<...>` which becomes ready as soon
/// as the the continuation chain has finished.
/// The signature of the future depends on the result type:
/// | Continuation type | Return type |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `std::future<void>` |
/// | `continuable_base with <Arg>` | `std::future<Arg>` |
/// | `continuable_base with <Args...>` | `std::future<std::tuple<Args...>>` |
///
/// \attention If exceptions are used, exceptions that are thrown, are forwarded
/// to the returned future. If there are no exceptions supported,
/// you shall not pass any errors to the end of the asynchronous
/// call chain!
/// Otherwise this will yield a trap that causes application exit.
///
/// \since 2.0.0
inline auto to_future() {
return [](auto&& continuable) {
return detail::transforms::to_future(
std::forward<decltype(continuable)>(continuable));
};
}
} // namespace transforms
/// \}
} // namespace cti
#endif // CONTINUABLE_OPERATIONS_SPLIT_HPP_INCLUDED

View File

@ -0,0 +1,68 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
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_TRANSFORMS_WAIT_HPP_INCLUDED
#define CONTINUABLE_TRANSFORMS_WAIT_HPP_INCLUDED
#include <utility>
#include <continuable/detail/transforms/wait.hpp>
namespace cti {
/// \ingroup Transforms
/// \{
namespace transforms {
/// Returns a transform that if applied to a continuable,
/// it will start the continuation chain and returns the result synchronously.
/// The current thread is blocked until the continuation chain is finished.
///
/// \returns Returns a value that is available immediately.
/// The signature of the future depends on the result type:
/// | Continuation type | Return type |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `void` |
/// | `continuable_base with <Arg>` | `Arg` |
/// | `continuable_base with <Args...>` | `std::tuple<Args...>` |
///
/// \attention If exceptions are used, exceptions that are thrown, are rethrown
/// synchronously.
///
/// \since 4.0.0
inline auto wait() {
return [](auto&& continuable) {
return detail::transforms::wait(
std::forward<decltype(continuable)>(continuable));
};
}
} // namespace transforms
/// \}
} // namespace cti
#endif // CONTINUABLE_TRANSFORMS_WAIT_HPP_INCLUDED

View File

@ -20,9 +20,37 @@
SOFTWARE.
**/
#include <memory>
#include <thread>
#include <continuable/continuable-transforms.hpp>
#include <continuable/continuable.hpp>
#include <continuable/external/asio.hpp>
#include <asio.hpp>
using namespace cti;
using namespace std::chrono_literals;
int main(int, char**) {
asio::io_context ioc(1);
asio::steady_timer t(ioc);
auto work = std::make_shared<asio::io_context::work>(ioc);
t.expires_after(1s);
std::thread th([&] {
ioc.run();
puts("io_context finished");
});
int res = t.async_wait(cti::use_continuable)
.then([] {
return 1;
})
.apply(transforms::wait());
puts("async_wait finished");
work.reset();
th.join();
return 0;
}

View File

@ -21,22 +21,27 @@
SOFTWARE.
**/
#include <chrono>
#include <future>
#include <thread>
#include <continuable/continuable-transforms.hpp>
#include <test-continuable.hpp>
using namespace cti;
using namespace cti::detail;
using namespace std::chrono_literals;
template <typename T>
bool is_ready(T& future) {
// Check that the future is ready
return future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
return future.wait_for(0s) == std::future_status::ready;
}
TYPED_TEST(single_dimension_tests, are_convertible_to_futures) {
TYPED_TEST(single_dimension_tests, to_future_test) {
{
auto future = this->supply().apply(cti::transforms::futurize());
auto future = this->supply().apply(cti::transforms::to_future());
ASSERT_TRUE(is_ready(future));
future.get();
}
@ -47,7 +52,7 @@ TYPED_TEST(single_dimension_tests, are_convertible_to_futures) {
// ...
return 0xFD;
})
.apply(cti::transforms::futurize());
.apply(cti::transforms::to_future());
ASSERT_TRUE(is_ready(future));
EXPECT_EQ(future.get(), 0xFD);
@ -56,19 +61,20 @@ TYPED_TEST(single_dimension_tests, are_convertible_to_futures) {
{
auto canary = std::make_tuple(0xFD, 0xF5);
auto future = this->supply().then([&] {
auto future = this->supply()
.then([&] {
// ...
return canary;
}) | cti::transforms::futurize();
})
.apply(cti::transforms::to_future());
ASSERT_TRUE(is_ready(future));
EXPECT_EQ(future.get(), canary);
}
}
TYPED_TEST(single_dimension_tests, are_flattable) {
auto continuation = this->supply_exception(supply_test_exception())
.apply(cti::transforms::flatten());
ASSERT_ASYNC_INCOMPLETION(std::move(continuation));
TYPED_TEST(single_dimension_tests, to_wait_test) {
{
this->supply().apply(cti::transforms::wait()); //
}
}