Start to port the traversal unit tests

This commit is contained in:
Denis Blank 2018-02-06 00:44:51 +01:00
parent a107a89991
commit 752bee6ea4
4 changed files with 1288 additions and 2 deletions

View File

@ -555,7 +555,8 @@ struct tuple_like_remapper<strategy_traverse_tag, M, Base<OldArgs...>,
template <typename... Args>
auto operator()(Args&&... args)
-> traits::void_t<typename invoke_result<M, OldArgs>::type...> {
-> traits::void_t<decltype(std::declval<M>(),
std::declval<OldArgs>())...> {
int dummy[] = {0, ((void)mapper_(std::forward<Args>(args)), 0)...};
(void)dummy;
}

View File

@ -16,7 +16,9 @@ foreach(STEP RANGE 4)
${CMAKE_CURRENT_LIST_DIR}/test-continuable-expected.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-erasure.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-regression.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-transforms.cpp)
${CMAKE_CURRENT_LIST_DIR}/test-continuable-transforms.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-traverse.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-traverse-async.cpp)
target_include_directories(${PROJECT_NAME}
PRIVATE

View File

@ -0,0 +1,477 @@
// Copyright (c) 2017 Denis Blank
// Copyright Andrey Semashev 2007 - 2013.
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// TODO Remove the boost dependency
/*
#include <hpx/config.hpp>
#include <hpx/util/atomic_count.hpp>
#include <hpx/util/lightweight_test.hpp>
#include <hpx/util/pack_traversal_async.hpp>
#include <hpx/util/tuple.hpp>
#include <hpx/util/unused.hpp>
#include <cstddef>
#include <cstdint>
#include <list>
#include <memory>
#include <set>
#include <type_traits>
#include <utility>
#include <vector>
#if defined(HPX_HAVE_CXX11_STD_ARRAY)
#include <array>
#endif
using hpx::util::async_traverse_complete_tag;
using hpx::util::async_traverse_detach_tag;
using hpx::util::async_traverse_visit_tag;
using hpx::util::make_tuple;
using hpx::util::traverse_pack_async;
using hpx::util::tuple;
/// A tag which isn't accepted by any mapper
struct not_accepted_tag
{
};
struct thread_safe_counter
{
typedef hpx::util::atomic_count type;
static unsigned int load(hpx::util::atomic_count const& counter) noexcept
{
return static_cast<unsigned int>(static_cast<long>(counter));
}
static void increment(hpx::util::atomic_count& counter) noexcept
{
++counter;
}
static unsigned int decrement(hpx::util::atomic_count& counter) noexcept
{
return static_cast<unsigned int>(--counter);
}
};
template <typename Derived, typename CounterPolicy = thread_safe_counter>
class intrusive_ref_counter;
template <typename Derived, typename CounterPolicy>
void intrusive_ptr_add_ref(
intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept;
template <typename Derived, typename CounterPolicy>
void intrusive_ptr_release(
intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept;
template <typename Derived, typename CounterPolicy>
class intrusive_ref_counter
{
private:
typedef typename CounterPolicy::type counter_type;
mutable counter_type ref_counter;
public:
intrusive_ref_counter() noexcept : ref_counter(1) {}
unsigned int use_count() const noexcept
{
return CounterPolicy::load(ref_counter);
}
protected:
~intrusive_ref_counter() = default;
friend void intrusive_ptr_add_ref<Derived, CounterPolicy>(
intrusive_ref_counter<Derived, CounterPolicy> const* p)
noexcept;
friend void intrusive_ptr_release<Derived, CounterPolicy>(
intrusive_ref_counter<Derived, CounterPolicy> const* p)
noexcept;
};
template <typename Derived, typename CounterPolicy>
inline void intrusive_ptr_add_ref(
intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept
{
CounterPolicy::increment(p->ref_counter);
}
template <typename Derived, typename CounterPolicy>
inline void intrusive_ptr_release(
intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept
{
if (CounterPolicy::decrement(p->ref_counter) == 0)
delete static_cast<Derived const*>(p);
}
template <typename Child>
class async_counter_base : public intrusive_ref_counter<Child>
{
std::size_t counter_ = 0;
public:
async_counter_base() = default;
virtual ~async_counter_base() {}
std::size_t const& counter() const noexcept
{
return counter_;
}
std::size_t& counter() noexcept
{
return counter_;
}
};
template <std::size_t ArgCount>
struct async_increasing_int_sync_visitor
: async_counter_base<async_increasing_int_sync_visitor<ArgCount>>
{
explicit async_increasing_int_sync_visitor(int dummy) {}
bool operator()(async_traverse_visit_tag, std::size_t i)
{
HPX_TEST_EQ(i, this->counter());
++this->counter();
return true;
}
template <typename N>
void operator()(async_traverse_detach_tag, std::size_t i, N&& next)
{
HPX_UNUSED(i);
HPX_UNUSED(next);
// Should never be called!
HPX_TEST(false);
}
template <typename T>
void operator()(async_traverse_complete_tag, T&& pack)
{
HPX_UNUSED(pack);
HPX_TEST_EQ(this->counter(), ArgCount);
++this->counter();
}
};
template <std::size_t ArgCount>
struct async_increasing_int_visitor
: async_counter_base<async_increasing_int_visitor<ArgCount>>
{
explicit async_increasing_int_visitor(int dummy) {}
bool operator()(async_traverse_visit_tag, std::size_t i) const
{
HPX_TEST_EQ(i, this->counter());
return false;
}
template <typename N>
void operator()(async_traverse_detach_tag, std::size_t i, N&& next)
{
HPX_UNUSED(i);
++this->counter();
std::forward<N>(next)();
}
template <typename T>
void operator()(async_traverse_complete_tag, T&& pack)
{
HPX_UNUSED(pack);
HPX_TEST_EQ(this->counter(), ArgCount);
++this->counter();
}
};
template <std::size_t ArgCount, typename... Args>
void test_async_traversal_base(Args&&... args)
{
// Test that every element is traversed in the correct order
// when we detach the control flow on every visit.
{
auto result = traverse_pack_async(
hpx::util::async_traverse_in_place_tag<
async_increasing_int_sync_visitor<ArgCount>>{},
42, args...);
HPX_TEST_EQ(result->counter(), ArgCount + 1U);
}
// Test that every element is traversed in the correct order
// when we detach the control flow on every visit.
{
auto result = traverse_pack_async(
hpx::util::async_traverse_in_place_tag<
async_increasing_int_visitor<ArgCount>>{},
42, args...);
HPX_TEST_EQ(result->counter(), ArgCount + 1U);
}
}
static void test_async_traversal()
{
// Just test everything using a casual int pack
test_async_traversal_base<4U>(not_accepted_tag{},
0U,
1U,
not_accepted_tag{},
2U,
3U,
not_accepted_tag{});
}
template <typename ContainerFactory>
void test_async_container_traversal_impl(ContainerFactory&& container_of)
{
// Test by passing a containers in the middle
test_async_traversal_base<4U>(0U, container_of(1U, 2U), 3U);
// Test by splitting the pack in two containers
test_async_traversal_base<4U>(container_of(0U, 1U), container_of(2U, 3U));
// Test by passing a huge containers to the traversal
test_async_traversal_base<4U>(container_of(0U, 1U, 2U, 3U));
}
template <typename T>
struct common_container_factory
{
template <typename... Args>
T operator()(Args&&... args)
{
return T{std::forward<Args>(args)...};
}
};
#if defined(HPX_HAVE_CXX11_STD_ARRAY)
template <typename T>
struct array_container_factory
{
template <typename... Args, typename Array = std::array<T, sizeof...(Args)>>
Array operator()(Args&&... args)
{
return Array{{std::forward<Args>(args)...}};
}
};
#endif
static void test_async_container_traversal()
{
{
common_container_factory<std::vector<std::size_t>> factory;
test_async_container_traversal_impl(factory);
}
{
common_container_factory<std::list<std::size_t>> factory;
test_async_container_traversal_impl(factory);
}
{
common_container_factory<std::set<std::size_t>> factory;
test_async_container_traversal_impl(factory);
}
#if defined(HPX_HAVE_CXX11_STD_ARRAY)
{
array_container_factory<std::size_t> factory;
test_async_container_traversal_impl(factory);
}
#endif
}
static void test_async_tuple_like_traversal()
{
// Test by passing a tuple in the middle
test_async_traversal_base<4U>(
not_accepted_tag{}, 0U, make_tuple(1U, not_accepted_tag{}, 2U), 3U);
// Test by splitting the pack in two tuples
test_async_traversal_base<4U>(
make_tuple(0U, not_accepted_tag{}, 1U), make_tuple(2U, 3U));
// Test by passing a huge tuple to the traversal
test_async_traversal_base<4U>(make_tuple(0U, 1U, 2U, 3U));
}
template <typename T,
typename... Args,
typename Vector = std::vector<typename std::decay<T>::type>>
Vector vector_of(T&& first, Args&&... args)
{
return Vector{std::forward<T>(first), std::forward<Args>(args)...};
}
static void test_async_mixed_traversal()
{
using container_t = std::vector<std::size_t>;
// Test hierarchies where container and tuple like types are mixed
test_async_traversal_base<4U>(
0U, hpx::util::make_tuple(container_t{1U, 2U}), 3U);
test_async_traversal_base<4U>(
hpx::util::make_tuple(
0U, vector_of(not_accepted_tag{}), vector_of(vector_of(1U))),
make_tuple(2U, 3U));
test_async_traversal_base<4U>(
vector_of(vector_of(make_tuple(0U, 1U, 2U, 3U))));
}
template <std::size_t ArgCount>
struct async_unique_sync_visitor
: async_counter_base<async_unique_sync_visitor<ArgCount>>
{
explicit async_unique_sync_visitor(int dummy) {}
bool operator()(async_traverse_visit_tag, std::unique_ptr<std::size_t>& i)
{
HPX_TEST_EQ(*i, this->counter());
++this->counter();
return true;
}
template <typename N>
void operator()(async_traverse_detach_tag,
std::unique_ptr<std::size_t>& i,
N&& next)
{
HPX_UNUSED(i);
HPX_UNUSED(next);
// Should never be called!
HPX_TEST(false);
}
template <typename T>
void operator()(async_traverse_complete_tag, T&& pack)
{
HPX_UNUSED(pack);
HPX_TEST_EQ(this->counter(), ArgCount);
++this->counter();
}
};
template <std::size_t ArgCount>
struct async_unique_visitor : async_counter_base<async_unique_visitor<ArgCount>>
{
explicit async_unique_visitor(int dummy) {}
bool operator()(async_traverse_visit_tag,
std::unique_ptr<std::size_t>& i) const
{
HPX_TEST_EQ(*i, this->counter());
return false;
}
template <typename N>
void operator()(async_traverse_detach_tag,
std::unique_ptr<std::size_t>& i,
N&& next)
{
HPX_UNUSED(i);
++this->counter();
std::forward<N>(next)();
}
template <typename T>
void operator()(async_traverse_complete_tag, T&& pack)
{
HPX_UNUSED(pack);
HPX_TEST_EQ(this->counter(), ArgCount);
++this->counter();
}
};
static void test_async_move_only_traversal()
{
auto const of = [](std::size_t i) {
return std::unique_ptr<std::size_t>(new std::size_t(i));
};
{
auto result = traverse_pack_async(
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);
}
{
auto result = traverse_pack_async(
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);
}
}
struct invalidate_visitor : async_counter_base<invalidate_visitor>
{
explicit invalidate_visitor(int dummy) {}
bool operator()(async_traverse_visit_tag, std::shared_ptr<int>& i) const
{
HPX_TEST_EQ(*i, 22);
return false;
}
template <typename N>
void operator()(async_traverse_detach_tag,
std::shared_ptr<int>& i,
N&& next)
{
HPX_UNUSED(i);
std::forward<N>(next)();
}
// Test whether the passed pack was passed as r-value reference
void operator()(async_traverse_complete_tag,
tuple<std::shared_ptr<int>>&& pack) const
{
// Invalidate the moved object
tuple<std::shared_ptr<int>> moved = std::move(pack);
HPX_UNUSED(moved);
}
};
// Check whether the arguments are invalidated (moved out) when called
static void test_async_complete_invalidation()
{
auto value = std::make_shared<int>(22);
auto frame = traverse_pack_async(
hpx::util::async_traverse_in_place_tag<invalidate_visitor>{},
42, value);
HPX_TEST_EQ(value.use_count(), 1U);
}
int main(int, char**)
{
test_async_traversal();
test_async_container_traversal();
test_async_tuple_like_traversal();
test_async_mixed_traversal();
test_async_move_only_traversal();
test_async_complete_invalidation();
return hpx::util::report_errors();
}
*/

View File

@ -0,0 +1,806 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v2.0.0
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 <algorithm>
#include <array>
#include <functional>
#include <list>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <continuable/continuable-traverse.hpp>
#include "test-continuable.hpp"
using std::get;
using std::make_tuple;
using std::tuple;
using cti::map_pack;
using cti::spread_this;
using cti::traverse_pack;
struct all_map_float {
template <typename T>
float operator()(T el) const {
return float(el + 1.f);
}
};
struct my_mapper {
template <typename T, typename std::enable_if<
std::is_same<T, int>::value>::type* = nullptr>
float operator()(T el) const {
return float(el + 1.f);
}
};
struct all_map {
template <typename T>
int operator()(T el) const {
return 0;
}
};
static void test_mixed_traversal() {
{
auto res =
map_pack(all_map_float{}, 0, 1.f, make_tuple(1.f, 3),
std::vector<std::vector<int>>{{1, 2}, {4, 5}},
std::vector<std::vector<float>>{{1.f, 2.f}, {4.f, 5.f}}, 2);
auto expected = make_tuple( // ...
1.f, 2.f, make_tuple(2.f, 4.f),
std::vector<std::vector<float>>{{2.f, 3.f}, {5.f, 6.f}},
std::vector<std::vector<float>>{{2.f, 3.f}, {5.f, 6.f}}, 3.f);
static_assert(std::is_same<decltype(res), decltype(expected)>::value,
"Type mismatch!");
EXPECT_TRUE((res == expected));
}
{
// Broken build regression tests:
traverse_pack(my_mapper{}, int(0), 1.f);
map_pack(all_map{}, 0, std::vector<int>{1, 2});
}
{
// Also a regression test
auto res = map_pack(all_map{}, std::vector<std::vector<int>>{{1, 2}});
EXPECT_EQ((res[0][0]), (0));
}
{
auto res = map_pack(
my_mapper{}, 0, 1.f,
make_tuple(1.f, 3, std::vector<std::vector<int>>{{1, 2}, {4, 5}},
std::vector<std::vector<float>>{{1.f, 2.f}, {4.f, 5.f}}),
2);
auto expected = make_tuple( // ...
1.f, 1.f,
make_tuple(1.f, 4.f,
std::vector<std::vector<float>>{{2.f, 3.f}, {5.f, 6.f}},
std::vector<std::vector<float>>{{1.f, 2.f}, {4.f, 5.f}}),
3.f);
static_assert(std::is_same<decltype(res), decltype(expected)>::value,
"Type mismatch!");
EXPECT_TRUE((res == expected));
}
{
int count = 0;
traverse_pack(
[&](int el) {
EXPECT_EQ((el), (count + 1));
count = el;
},
1, make_tuple(2, 3, std::vector<std::vector<int>>{{4, 5}, {6, 7}}));
EXPECT_EQ((count), (7));
}
return;
}
struct my_unwrapper {
template <typename T,
typename std::enable_if<is_future<T>::value>::type* = nullptr>
auto operator()(T future) const -> typename future_traits<T>::result_type {
return future.get();
}
};
static void test_mixed_early_unwrapping() {
{
auto res = map_pack(my_unwrapper{}, // ...
0, 1, make_ready_future(3),
make_tuple(make_ready_future(4), make_ready_future(5)));
auto expected = make_tuple(0, 1, 3, make_tuple(4, 5));
static_assert(std::is_same<decltype(res), decltype(expected)>::value,
"Type mismatch!");
EXPECT_TRUE((res == expected));
}
}
template <typename T>
struct my_allocator {
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = T const*;
using reference = T&;
using const_reference = T const&;
unsigned state_;
explicit my_allocator(unsigned state) : state_(state) {
return;
}
template <typename O>
my_allocator(my_allocator<O> const& other) : state_(other.state_) {
return;
}
template <typename O>
my_allocator& operator=(my_allocator<O> const& other) {
state_ = other.state_;
return *this;
}
template <typename O>
struct rebind {
using other = my_allocator<O>;
};
pointer allocate(size_type n, void const* hint = nullptr) {
return std::allocator<T>{}.allocate(n, hint);
}
void deallocate(pointer p, size_type n) {
return std::allocator<T>{}.deallocate(p, n);
}
};
static void test_mixed_container_remap() {
// Traits
{
// TODO Enable this
// using detail::container_remapping::has_push_back;
// EXPECT_EQ((has_push_back<std::vector<int>, int>::value), true);
// EXPECT_EQ((has_push_back<int, int>::value), false);
}
// Rebind
{
auto const remapper = [](unsigned short i) -> unsigned long {
return i - 1;
};
// Rebinds the values
{
std::vector<unsigned short> source = {1, 2, 3};
std::vector<unsigned long> dest = map_pack(remapper, source);
EXPECT_TRUE((dest == decltype(dest){0, 1, 2}));
}
// Rebinds the allocator
{
static unsigned const canary = 78787;
my_allocator<unsigned short> allocator(canary);
std::vector<unsigned short, my_allocator<unsigned short>> source(
allocator);
// Empty
{
std::vector<unsigned long, my_allocator<unsigned long>> remapped =
map_pack(remapper, source);
EXPECT_EQ((remapped.get_allocator().state_), (canary));
}
// Non empty
source.push_back(1);
{
std::vector<unsigned long, my_allocator<unsigned long>> remapped =
map_pack(remapper, source);
EXPECT_EQ((remapped.get_allocator().state_), (canary));
}
}
}
}
struct mytester {
using traversor_type = mytester;
int operator()(int) {
return 0;
}
};
struct my_int_mapper {
template <typename T, typename std::enable_if<
std::is_same<T, int>::value>::type* = nullptr>
float operator()(T el) const {
return float(el + 1.f);
}
};
static void test_mixed_fall_through() {
traverse_pack(my_int_mapper{}, int(0),
std::vector<tuple<float, float>>{make_tuple(1.f, 2.f)},
make_tuple(std::vector<float>{1.f, 2.f}));
traverse_pack(my_int_mapper{}, int(0),
std::vector<std::vector<float>>{{1.f, 2.f}},
make_tuple(1.f, 2.f));
auto res1 = map_pack(my_int_mapper{}, int(0),
std::vector<std::vector<float>>{{1.f, 2.f}},
make_tuple(77.f, 2));
auto res2 = map_pack(
[](int) {
// ...
return 0;
},
1, std::vector<int>{2, 3});
}
class counter_mapper {
std::reference_wrapper<int> counter_;
public:
explicit counter_mapper(int& counter) : counter_(counter) {
}
template <typename T>
void operator()(T el) const {
++counter_.get();
}
};
struct test_tag_1 {};
struct test_tag_2 {};
struct test_tag_3 {};
class counter_mapper_rejecting_non_tag_1 {
std::reference_wrapper<int> counter_;
public:
explicit counter_mapper_rejecting_non_tag_1(int& counter)
: counter_(counter) {
}
void operator()(test_tag_1) {
++counter_.get();
}
};
struct tag_shift_mapper {
test_tag_2 operator()(test_tag_1) const {
return {};
}
test_tag_3 operator()(test_tag_2) const {
return {};
}
test_tag_1 operator()(test_tag_3) const {
return {};
}
float operator()(int) const {
return 0.f;
}
};
class counter_mapper_rejecting_non_tag_1_sfinae {
std::reference_wrapper<int> counter_;
public:
explicit counter_mapper_rejecting_non_tag_1_sfinae(int& counter)
: counter_(counter) {
}
template <typename T, typename std::enable_if<
std::is_same<typename std::decay<T>::type,
test_tag_1>::value>::type* = nullptr>
void operator()(T) {
++counter_.get();
}
};
static void test_strategic_traverse() {
// Every element in the pack is visited
{
int counter = 0;
counter_mapper mapper(counter);
traverse_pack(mapper, test_tag_1{}, test_tag_2{}, test_tag_3{});
EXPECT_EQ(counter, 3);
}
// Every element in the pack is visited from left to right
{
int counter = 0;
traverse_pack(
[&](int el) {
EXPECT_EQ(counter, el);
++counter;
},
0, 1, 2, 3);
EXPECT_EQ(counter, 4);
}
// Elements accepted by the mapper aren't traversed:
// - Signature
{
int counter = 0;
counter_mapper_rejecting_non_tag_1 mapper(counter);
traverse_pack(mapper, test_tag_1{}, test_tag_2{}, test_tag_3{});
EXPECT_EQ(counter, 1);
}
// - SFINAE
{
int counter = 0;
counter_mapper_rejecting_non_tag_1_sfinae mapper(counter);
traverse_pack(mapper, test_tag_1{}, test_tag_2{}, test_tag_3{});
EXPECT_EQ(counter, 1);
}
// Remapping works across values
{
tuple<int, int, int> res = map_pack([](int i) { return i + 1; }, 0, 1, 2);
auto expected = make_tuple(1, 2, 3);
EXPECT_TRUE((res == expected));
}
// Remapping works across types
{
tag_shift_mapper mapper;
tuple<float, test_tag_2, test_tag_3, test_tag_1> res =
map_pack(mapper, 1, test_tag_1{}, test_tag_2{}, test_tag_3{});
EXPECT_EQ(get<0>(res), 0.f);
}
// Remapping works with move-only objects
{
std::unique_ptr<int> p1(new int(1));
std::unique_ptr<int> p2(new int(2));
std::unique_ptr<int> p3(new int(3));
tuple<std::unique_ptr<unsigned>, std::unique_ptr<unsigned>,
std::unique_ptr<unsigned>>
res = map_pack(
// Since we pass the unique_ptr's as r-value,
// those should be passed as r-values to the mapper.
[](std::unique_ptr<int>&& ptr) {
// We explicitly move the ownership here
std::unique_ptr<int> owned = std::move(ptr);
return std::unique_ptr<unsigned>(new unsigned(*owned + 1));
},
std::move(p1), std::move(p2), std::move(p3));
// We expect the ownership of p1 - p3 to be invalid
EXPECT_TRUE((!bool(p1)));
EXPECT_TRUE((!bool(p2)));
EXPECT_TRUE((!bool(p3)));
EXPECT_EQ((*get<0>(res)), 2U);
EXPECT_EQ((*get<1>(res)), 3U);
EXPECT_EQ((*get<2>(res)), 4U);
}
// Move only types contained in a pack which was passed as l-value
// reference is forwarded to the mapper as reference too.
{
std::vector<std::unique_ptr<int>> container;
container.push_back(std::unique_ptr<int>(new int(3)));
std::vector<int> res =
map_pack([](std::unique_ptr<int>& p) { return *p; }, container);
EXPECT_EQ(res.size(), 1U);
EXPECT_EQ(res[0], 3);
}
// Single object remapping returns the value itself without any boxing
{
int res = map_pack([](int i) { return i; }, 1);
EXPECT_EQ(res, 1);
}
// Make it possible to pass move only objects in as reference,
// while returning those as reference.
{
std::unique_ptr<int> ptr(new int(7));
std::unique_ptr<int> const& ref = map_pack(
[](std::unique_ptr<int> const& ref) -> std::unique_ptr<int> const& {
// ...
return ref;
},
ptr);
EXPECT_EQ(*ref, 7);
*ptr = 0;
EXPECT_EQ(*ref, 0);
}
// Multiple args: Make it possible to pass move only objects in
// as reference, while returning those as reference.
{
std::unique_ptr<int> ptr1(new int(6));
std::unique_ptr<int> ptr2(new int(7));
tuple<std::unique_ptr<int> const&, std::unique_ptr<int> const&> ref =
map_pack(
[](std::unique_ptr<int> const& ref) -> std::unique_ptr<int> const& {
// ...
return ref;
},
ptr1, ptr2);
EXPECT_EQ((*get<0>(ref)), 6);
EXPECT_EQ((*get<1>(ref)), 7);
*ptr1 = 1;
*ptr2 = 2;
EXPECT_EQ((*get<0>(ref)), 1);
EXPECT_EQ((*get<1>(ref)), 2);
}
}
static void test_strategic_container_traverse() {
// Every element in the container is visited
// - Plain container
{
int counter = 0;
counter_mapper mapper(counter);
std::vector<int> container;
container.resize(100);
traverse_pack(mapper, std::move(container));
EXPECT_EQ(counter, 100);
}
// - Nested container
{
int counter = 0;
counter_mapper mapper(counter);
std::vector<std::vector<int>> container;
for (unsigned i = 0; i < 10; ++i) {
std::vector<int> nested;
nested.resize(10);
container.push_back(nested);
}
traverse_pack(mapper, std::move(container));
EXPECT_EQ(counter, 100);
}
// Every element in the container is visited from left to right
{
int counter = 0;
traverse_pack(
[&](int el) {
EXPECT_EQ(counter, el);
++counter;
},
std::vector<int>{0, 1}, std::vector<std::vector<int>>{{2, 3}, {4, 5}});
EXPECT_EQ(counter, 6);
}
// The container type itself is changed
// - Plain container
{
std::vector<int> container{1, 2, 3};
std::vector<float> res =
map_pack([](int) { return 0.f; }, std::move(container));
EXPECT_EQ(res.size(), 3U);
}
// - Nested container
{
std::vector<std::vector<int>> container;
std::vector<std::vector<float>> res =
map_pack([](int) { return 0.f; }, std::move(container));
}
// - Move only container
{
std::vector<std::unique_ptr<int>> container;
container.push_back(std::unique_ptr<int>(new int(5)));
std::vector<int> res = map_pack(
[](std::unique_ptr<int>&& ptr) { return *ptr; }, std::move(container));
EXPECT_EQ(res.size(), 1U);
EXPECT_EQ(res[0], 5);
}
// Every element in the container is remapped
// - Plain container
{
std::vector<int> container(100, 1);
auto res = map_pack([](int i) { return 2; }, std::move(container));
EXPECT_TRUE(
(std::all_of(res.begin(), res.end(), [](int i) { return i == 2; })));
}
// - Nested container
{
std::vector<std::list<int>> container;
for (unsigned i = 0; i < 10; ++i) {
std::list<int> nested(10, 1);
container.push_back(nested);
}
auto res = map_pack([](int i) { return 2; }, std::move(container));
EXPECT_TRUE(
(std::all_of(res.begin(), res.end(), [](std::list<int> const& nested) {
return std::all_of(nested.begin(), nested.end(),
[](int i) { return i == 2; });
})));
}
/// - Ensure correct container remapping when returning references
{
// l-value references
{
std::vector<std::unique_ptr<int>> container;
container.push_back(std::unique_ptr<int>(new int(7)));
std::vector<int> res = map_pack(
[](std::unique_ptr<int> const& ref) -> int const& {
// ...
return *ref;
},
container);
EXPECT_EQ(res.size(), 1U);
EXPECT_EQ(res[0], 7);
}
// r-value references
{
std::vector<std::unique_ptr<std::unique_ptr<int>>> container;
container.push_back(std::unique_ptr<std::unique_ptr<int>>(
new std::unique_ptr<int>(new int(7))));
std::vector<std::unique_ptr<int>> res = map_pack(
[](std::unique_ptr<std::unique_ptr<int>> &
ref) -> std::unique_ptr<int>&& {
// ...
return std::move(*ref);
},
container);
EXPECT_EQ(res.size(), 1U);
EXPECT_EQ((*res[0]), 7);
}
}
}
static void test_strategic_tuple_like_traverse() {
// Every element in the tuple like type is visited
{
int counter = 0;
counter_mapper mapper(counter);
traverse_pack(mapper, make_tuple(test_tag_1{}, test_tag_2{}, test_tag_3{}));
EXPECT_EQ(counter, 3);
}
// Every element in the tuple like type is visited from left to right
{
int counter = 0;
traverse_pack(
[&](int el) {
EXPECT_EQ(counter, el);
++counter;
},
make_tuple(0, 1), make_tuple(make_tuple(2, 3), make_tuple(4, 5)),
make_tuple(make_tuple(make_tuple(6, 7))));
EXPECT_EQ(counter, 8);
}
// The container tuple like type itself is changed
{
tag_shift_mapper mapper;
tuple<float, test_tag_2, test_tag_3, test_tag_1> res = map_pack(
mapper, make_tuple(1, test_tag_1{}, test_tag_2{}, test_tag_3{}));
EXPECT_EQ(get<0>(res), 0.f);
}
// Every element in the tuple like type is remapped
{
tuple<float, float, float> res =
map_pack([](int) { return 1.f; }, make_tuple(0, 0, 0));
auto expected = make_tuple(1.f, 1.f, 1.f);
static_assert(std::is_same<decltype(res), decltype(expected)>::value,
"Type mismatch!");
EXPECT_TRUE((res == expected));
}
// Fixed size homogeneous container
{
std::array<int, 3> values{{1, 2, 3}};
std::array<float, 3> res = map_pack([](int) { return 1.f; }, values);
EXPECT_TRUE((res == std::array<float, 3>{{1.f, 1.f, 1.f}}));
}
// Make it possible to pass tuples containing move only objects
// in as reference, while returning those as reference.
{
auto value = make_tuple(std::unique_ptr<int>(new int(6)),
std::unique_ptr<int>(new int(7)));
tuple<std::unique_ptr<int> const&, std::unique_ptr<int> const&> ref =
map_pack(
[](std::unique_ptr<int> const& ref) -> std::unique_ptr<int> const& {
// ...
return ref;
},
value);
EXPECT_EQ((*get<0>(ref)), 6);
EXPECT_EQ((*get<1>(ref)), 7);
(*get<0>(ref)) = 1;
(*get<1>(ref)) = 2;
EXPECT_EQ((*get<0>(ref)), 1);
EXPECT_EQ((*get<1>(ref)), 2);
}
}
/// A mapper which duplicates the given element
struct duplicate_mapper {
template <typename T>
auto operator()(T arg) -> decltype(spread_this(arg, arg)) {
return spread_this(arg, arg);
}
};
/// A mapper which removes the current element
struct zero_mapper {
template <typename T>
auto operator()(T arg) -> decltype(spread_this()) {
return spread_this();
}
};
static void test_spread_traverse() {
// 1:2 mappings (multiple arguments)
{
tuple<int, int, int, int> res = map_pack(duplicate_mapper{}, 1, 2);
auto expected = make_tuple(1, 1, 2, 2);
EXPECT_TRUE((res == expected));
}
// 1:0 mappings
{
using Result = decltype(map_pack(zero_mapper{}, 0, 1, 2));
static_assert(std::is_void<Result>::value, "Failed...");
}
}
static void test_spread_container_traverse() {
// 1:2 mappings (multiple arguments)
{
std::vector<tuple<int, int>> res =
map_pack(duplicate_mapper{}, std::vector<int>{1});
std::vector<tuple<int, int>> expected;
expected.push_back(make_tuple(1, 1));
EXPECT_TRUE((res == expected));
}
// 1:0 mappings
{
using Result = decltype(map_pack(zero_mapper{}, std::vector<int>{1}));
static_assert(std::is_void<Result>::value, "Failed...");
}
}
static void test_spread_tuple_like_traverse() {
// 1:2 mappings (multiple arguments)
{
tuple<tuple<int, int, int, int>> res =
map_pack(duplicate_mapper{}, make_tuple(make_tuple(1, 2)));
tuple<tuple<int, int, int, int>> expected =
make_tuple(make_tuple(1, 1, 2, 2));
EXPECT_TRUE((res == expected));
}
// 1:0 mappings
{
using Result =
decltype(map_pack(zero_mapper{}, make_tuple(make_tuple(1, 2), 1), 1));
static_assert(std::is_void<Result>::value, "Failed...");
}
// 1:2 mappings (multiple arguments)
{
std::array<int, 4> res =
map_pack(duplicate_mapper{}, std::array<int, 2>{{1, 2}});
std::array<int, 4> expected{{1, 1, 2, 2}};
EXPECT_TRUE((res == expected));
}
// 1:0 mappings
{
using Result =
decltype(map_pack(zero_mapper{}, std::array<int, 2>{{1, 2}}));
static_assert(std::is_void<Result>::value, "Failed...");
}
}
/*
TODO Convert this to gtest
int main(int, char**) {
test_mixed_traversal();
test_mixed_early_unwrapping();
test_mixed_container_remap();
test_mixed_fall_through();
test_strategic_traverse();
test_strategic_container_traverse();
test_strategic_tuple_like_traverse();
test_spread_traverse();
test_spread_container_traverse();
test_spread_tuple_like_traverse();
return report_errors();
}
*/