mirror of
https://github.com/Naios/continuable.git
synced 2025-12-08 01:36:46 +08:00
Start to port the traversal unit tests
This commit is contained in:
parent
a107a89991
commit
752bee6ea4
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
477
test/unit-test/test-continuable-traverse-async.cpp
Normal file
477
test/unit-test/test-continuable-traverse-async.cpp
Normal 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();
|
||||
}
|
||||
*/
|
||||
806
test/unit-test/test-continuable-traverse.cpp
Normal file
806
test/unit-test/test-continuable-traverse.cpp
Normal 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();
|
||||
}
|
||||
*/
|
||||
Loading…
x
Reference in New Issue
Block a user