mirror of
https://github.com/Naios/continuable.git
synced 2025-12-08 01:36:46 +08:00
More work on porting the async traversal
This commit is contained in:
parent
16dd2b0cb9
commit
04111c0bc3
@ -105,7 +105,7 @@ using detail::traversal::async_traverse_in_place_tag;
|
||||
/// asynchronously. Nested objects inside containers and
|
||||
/// tuple like types are traversed recursively.
|
||||
///
|
||||
/// \returns A boost::intrusive_ptr that references an instance of
|
||||
/// \returns A std::shared_ptr that references an instance of
|
||||
/// the given visitor object.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
|
||||
@ -133,9 +133,7 @@ public:
|
||||
}
|
||||
|
||||
/// We require a virtual base
|
||||
~async_traversal_frame() override {
|
||||
assert(finished_.load());
|
||||
}
|
||||
virtual ~async_traversal_frame() override = default;
|
||||
|
||||
template <typename MapperArg>
|
||||
explicit async_traversal_frame(async_traverse_in_place_tag<Visitor>,
|
||||
@ -161,13 +159,14 @@ public:
|
||||
/// when it's called later.
|
||||
template <typename T, typename Hierarchy>
|
||||
void async_continue(T&& value, Hierarchy&& hierarchy) {
|
||||
// Create a self reference
|
||||
boost::intrusive_ptr<async_traversal_frame> self(this);
|
||||
// Cast the frame up
|
||||
auto frame = std::static_pointer_cast<async_traversal_frame>(
|
||||
this->shared_from_this());
|
||||
|
||||
// Create a callable object which resumes the current
|
||||
// traversal when it's called.
|
||||
auto resumable = make_resume_traversal_callable(
|
||||
std::move(self), std::forward<Hierarchy>(hierarchy));
|
||||
std::move(frame), std::forward<Hierarchy>(hierarchy));
|
||||
|
||||
// Invoke the visitor with the current value and the
|
||||
// callable object to resume the control flow.
|
||||
@ -197,12 +196,12 @@ struct static_async_range {
|
||||
}
|
||||
|
||||
static_async_range(static_async_range const& rhs) = default;
|
||||
static_async_range(static_async_range&& rhs) : target_(rhs.target_) {
|
||||
static_async_range(static_async_range&& rhs) noexcept : target_(rhs.target_) {
|
||||
rhs.target_ = nullptr;
|
||||
}
|
||||
|
||||
static_async_range& operator=(static_async_range const& rhs) = default;
|
||||
static_async_range& operator=(static_async_range&& rhs) {
|
||||
static_async_range& operator=(static_async_range&& rhs) noexcept {
|
||||
if (&rhs != this) {
|
||||
target_ = rhs.target_;
|
||||
rhs.target_ = nullptr;
|
||||
@ -210,18 +209,16 @@ struct static_async_range {
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator*() const noexcept
|
||||
-> decltype(std::get<Begin>(*target_)) {
|
||||
constexpr decltype(auto) operator*() const noexcept {
|
||||
return std::get<Begin>(*target_);
|
||||
}
|
||||
|
||||
template <std::size_t Position>
|
||||
constexpr static_async_range<Target, Position, End> relocate() const
|
||||
noexcept {
|
||||
constexpr auto relocate() const noexcept {
|
||||
return static_async_range<Target, Position, End>{target_};
|
||||
}
|
||||
|
||||
constexpr static_async_range<Target, Begin + 1, End> next() const noexcept {
|
||||
constexpr auto next() const noexcept {
|
||||
return static_async_range<Target, Begin + 1, End>{target_};
|
||||
}
|
||||
|
||||
@ -243,12 +240,12 @@ struct static_async_range<Target, Begin, Begin> {
|
||||
};
|
||||
|
||||
/// Returns a static range for the given type
|
||||
template <typename T, typename Range = static_async_range<
|
||||
typename std::decay<T>::type, 0U,
|
||||
std::tuple_size<typename std::decay<T>::type>::value>>
|
||||
Range make_static_range(T&& element) {
|
||||
auto pointer = std::addressof(element);
|
||||
return Range{pointer};
|
||||
template <typename T>
|
||||
auto make_static_range(T&& element) {
|
||||
using range_t = static_async_range<std::decay_t<T>, 0U,
|
||||
std::tuple_size<std::decay_t<T>>::value>;
|
||||
|
||||
return range_t{std::addressof(element)};
|
||||
}
|
||||
|
||||
template <typename Begin, typename Sentinel>
|
||||
@ -282,9 +279,10 @@ using dynamic_async_range_of_t = dynamic_async_range<
|
||||
typename std::decay<decltype(std::end(std::declval<T>()))>::type>;
|
||||
|
||||
/// Returns a dynamic range for the given type
|
||||
template <typename T, typename Range = dynamic_async_range_of_t<T>>
|
||||
Range make_dynamic_async_range(T&& element) {
|
||||
return Range{std::begin(element), std::end(element)};
|
||||
template <typename T>
|
||||
auto make_dynamic_async_range(T&& element) {
|
||||
using range_t = dynamic_async_range_of_t<T>;
|
||||
return range_t{std::begin(element), std::end(element)};
|
||||
}
|
||||
|
||||
/// Represents a particular point in a asynchronous traversal hierarchy
|
||||
@ -316,8 +314,7 @@ public:
|
||||
/// Creates a new traversal point which
|
||||
template <typename Parent>
|
||||
auto push(Parent&& parent)
|
||||
-> async_traversal_point<Frame, typename std::decay<Parent>::type,
|
||||
Hierarchy...> {
|
||||
-> async_traversal_point<Frame, std::decay_t<Parent>, Hierarchy...> {
|
||||
// Create a new hierarchy which contains the
|
||||
// the parent (the last traversed element).
|
||||
auto hierarchy = std::tuple_cat(
|
||||
@ -413,6 +410,7 @@ public:
|
||||
current.template relocate<Sequence>()),
|
||||
0)...};
|
||||
(void)dummy;
|
||||
(void)current;
|
||||
}
|
||||
|
||||
/// Traverse a static range
|
||||
@ -514,14 +512,11 @@ template <typename Visitor, typename... Args>
|
||||
struct async_traversal_types {
|
||||
/// Deduces to the async traversal frame type of the given
|
||||
/// traversal arguments and mapper
|
||||
using frame_type = async_traversal_frame<typename std::decay<Visitor>::type,
|
||||
typename std::decay<Args>::type...>;
|
||||
|
||||
/// The type of the frame pointer
|
||||
using frame_pointer_type = boost::intrusive_ptr<frame_type>;
|
||||
using frame_t = async_traversal_frame<typename std::decay<Visitor>::type,
|
||||
typename std::decay<Args>::type...>;
|
||||
|
||||
/// The type of the demoted visitor type
|
||||
using visitor_pointer_type = boost::intrusive_ptr<Visitor>;
|
||||
using visitor_t = Visitor;
|
||||
};
|
||||
|
||||
template <typename Visitor, typename VisitorArg, typename... Args>
|
||||
@ -530,30 +525,40 @@ struct async_traversal_types<async_traverse_in_place_tag<Visitor>, VisitorArg,
|
||||
: async_traversal_types<Visitor, Args...> {};
|
||||
|
||||
/// Traverses the given pack with the given mapper
|
||||
template <typename Visitor, typename... Args,
|
||||
typename types = async_traversal_types<Visitor, Args...>>
|
||||
auto apply_pack_transform_async(Visitor&& visitor, Args&&... args) ->
|
||||
typename types::visitor_pointer_type {
|
||||
// Create the frame on the heap which stores the arguments
|
||||
// to traverse asynchronous.
|
||||
auto frame = [&] {
|
||||
auto ptr = new typename types::frame_type(std::forward<Visitor>(visitor),
|
||||
std::forward<Args>(args)...);
|
||||
template <typename Visitor, typename... Args>
|
||||
auto apply_pack_transform_async(Visitor&& visitor, Args&&... args) {
|
||||
|
||||
// Create an intrusive_ptr from the heap object, don't increase
|
||||
// reference count (it's already 'one').
|
||||
return typename types::frame_pointer_type(ptr, false);
|
||||
}();
|
||||
// Provide the frame and visitor type
|
||||
using types = async_traversal_types<Visitor, Args...>;
|
||||
|
||||
// Check whether the visitor inherits enable_shared_from_this
|
||||
static_assert(
|
||||
std::is_base_of<std::enable_shared_from_this<typename types::visitor_t>,
|
||||
typename types::visitor_t>::value,
|
||||
"The visitor must inherit std::enable_shared_from_this!");
|
||||
|
||||
// Check whether the visitor is virtual destructible
|
||||
static_assert(std::has_virtual_destructor<typename types::visitor_t>::value,
|
||||
"The visitor must have a virtual destructor!");
|
||||
|
||||
// Create the frame on the heap which stores the arguments
|
||||
// to traverse asynchronous. It persists until the
|
||||
// traversal frame isn't referenced anymore.
|
||||
auto frame = std::make_shared<types::frame_t>(std::forward<Visitor>(visitor),
|
||||
std::forward<Args>(args)...);
|
||||
|
||||
// Create a static range for the top level tuple
|
||||
auto range = make_static_range(frame->head());
|
||||
auto range = std::make_tuple(make_static_range(frame->head()));
|
||||
|
||||
auto resumer =
|
||||
make_resume_traversal_callable(frame, std::make_tuple(std::move(range)));
|
||||
// Create a resumer to start the asynchronous traversal
|
||||
auto resumer = make_resume_traversal_callable(frame, std::move(range));
|
||||
|
||||
// Start the asynchronous traversal
|
||||
resumer();
|
||||
return frame;
|
||||
|
||||
// Cast the shared_ptr down to the given visitor type
|
||||
// for implementation invisibility
|
||||
return std::static_pointer_cast<types::visitor_t>(std::move(frame));
|
||||
}
|
||||
} // namespace traversal
|
||||
} // namespace detail
|
||||
|
||||
@ -1,3 +1,70 @@
|
||||
|
||||
/*
|
||||
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 <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <continuable/continuable-traverse-async.hpp>
|
||||
|
||||
#include "test-continuable.hpp"
|
||||
|
||||
using std::get;
|
||||
using std::make_tuple;
|
||||
using std::tuple;
|
||||
|
||||
using cti::async_traverse_complete_tag;
|
||||
using cti::async_traverse_detach_tag;
|
||||
using cti::async_traverse_visit_tag;
|
||||
using cti::traverse_pack_async;
|
||||
|
||||
struct my_visitor : std::enable_shared_from_this<my_visitor> {
|
||||
virtual ~my_visitor() = default;
|
||||
|
||||
bool operator()(async_traverse_visit_tag, std::size_t i) {
|
||||
(void)i;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename N>
|
||||
void operator()(async_traverse_detach_tag, std::size_t i, N&& next) {
|
||||
(void)i;
|
||||
next();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void operator()(async_traverse_complete_tag, T&& pack) {
|
||||
(void)pack;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(misc_as_test, my_test) {
|
||||
cti::traverse_pack_async(my_visitor{}, 0, 1, 2);
|
||||
}
|
||||
|
||||
// Copyright (c) 2017 Denis Blank
|
||||
// Copyright Andrey Semashev 2007 - 2013.
|
||||
//
|
||||
@ -142,7 +209,7 @@ struct async_increasing_int_sync_visitor
|
||||
|
||||
bool operator()(async_traverse_visit_tag, std::size_t i)
|
||||
{
|
||||
HPX_TEST_EQ(i, this->counter());
|
||||
EXPECT_EQ(i, this->counter());
|
||||
++this->counter();
|
||||
return true;
|
||||
}
|
||||
@ -162,7 +229,7 @@ struct async_increasing_int_sync_visitor
|
||||
{
|
||||
HPX_UNUSED(pack);
|
||||
|
||||
HPX_TEST_EQ(this->counter(), ArgCount);
|
||||
EXPECT_EQ(this->counter(), ArgCount);
|
||||
++this->counter();
|
||||
}
|
||||
};
|
||||
@ -175,7 +242,7 @@ struct async_increasing_int_visitor
|
||||
|
||||
bool operator()(async_traverse_visit_tag, std::size_t i) const
|
||||
{
|
||||
HPX_TEST_EQ(i, this->counter());
|
||||
EXPECT_EQ(i, this->counter());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -193,7 +260,7 @@ struct async_increasing_int_visitor
|
||||
{
|
||||
HPX_UNUSED(pack);
|
||||
|
||||
HPX_TEST_EQ(this->counter(), ArgCount);
|
||||
EXPECT_EQ(this->counter(), ArgCount);
|
||||
++this->counter();
|
||||
}
|
||||
};
|
||||
@ -208,7 +275,7 @@ void test_async_traversal_base(Args&&... args)
|
||||
hpx::util::async_traverse_in_place_tag<
|
||||
async_increasing_int_sync_visitor<ArgCount>>{},
|
||||
42, args...);
|
||||
HPX_TEST_EQ(result->counter(), ArgCount + 1U);
|
||||
EXPECT_EQ(result->counter(), ArgCount + 1U);
|
||||
}
|
||||
|
||||
// Test that every element is traversed in the correct order
|
||||
@ -218,7 +285,7 @@ void test_async_traversal_base(Args&&... args)
|
||||
hpx::util::async_traverse_in_place_tag<
|
||||
async_increasing_int_visitor<ArgCount>>{},
|
||||
42, args...);
|
||||
HPX_TEST_EQ(result->counter(), ArgCount + 1U);
|
||||
EXPECT_EQ(result->counter(), ArgCount + 1U);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +404,7 @@ struct async_unique_sync_visitor
|
||||
|
||||
bool operator()(async_traverse_visit_tag, std::unique_ptr<std::size_t>& i)
|
||||
{
|
||||
HPX_TEST_EQ(*i, this->counter());
|
||||
EXPECT_EQ(*i, this->counter());
|
||||
++this->counter();
|
||||
return true;
|
||||
}
|
||||
@ -359,7 +426,7 @@ struct async_unique_sync_visitor
|
||||
{
|
||||
HPX_UNUSED(pack);
|
||||
|
||||
HPX_TEST_EQ(this->counter(), ArgCount);
|
||||
EXPECT_EQ(this->counter(), ArgCount);
|
||||
++this->counter();
|
||||
}
|
||||
};
|
||||
@ -372,7 +439,7 @@ struct async_unique_visitor : async_counter_base<async_unique_visitor<ArgCount>>
|
||||
bool operator()(async_traverse_visit_tag,
|
||||
std::unique_ptr<std::size_t>& i) const
|
||||
{
|
||||
HPX_TEST_EQ(*i, this->counter());
|
||||
EXPECT_EQ(*i, this->counter());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -392,7 +459,7 @@ struct async_unique_visitor : async_counter_base<async_unique_visitor<ArgCount>>
|
||||
{
|
||||
HPX_UNUSED(pack);
|
||||
|
||||
HPX_TEST_EQ(this->counter(), ArgCount);
|
||||
EXPECT_EQ(this->counter(), ArgCount);
|
||||
++this->counter();
|
||||
}
|
||||
};
|
||||
@ -408,7 +475,7 @@ static void test_async_move_only_traversal()
|
||||
hpx::util::async_traverse_in_place_tag<
|
||||
async_unique_sync_visitor<4>>{},
|
||||
42, of(0), of(1), of(2), of(3));
|
||||
HPX_TEST_EQ(result->counter(), 5U);
|
||||
EXPECT_EQ(result->counter(), 5U);
|
||||
}
|
||||
|
||||
{
|
||||
@ -416,7 +483,7 @@ static void test_async_move_only_traversal()
|
||||
hpx::util::async_traverse_in_place_tag<
|
||||
async_unique_visitor<4>>{},
|
||||
42, of(0), of(1), of(2), of(3));
|
||||
HPX_TEST_EQ(result->counter(), 5U);
|
||||
EXPECT_EQ(result->counter(), 5U);
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,7 +493,7 @@ struct invalidate_visitor : async_counter_base<invalidate_visitor>
|
||||
|
||||
bool operator()(async_traverse_visit_tag, std::shared_ptr<int>& i) const
|
||||
{
|
||||
HPX_TEST_EQ(*i, 22);
|
||||
EXPECT_EQ(*i, 22);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -460,7 +527,7 @@ static void test_async_complete_invalidation()
|
||||
hpx::util::async_traverse_in_place_tag<invalidate_visitor>{},
|
||||
42, value);
|
||||
|
||||
HPX_TEST_EQ(value.use_count(), 1U);
|
||||
EXPECT_EQ(value.use_count(), 1U);
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user