This is a follow-up question for A recursive_transform Template Function Implementation with std::invocable
concept in C++, A recursive_transform Template Function with Execution Policy, A recursive_transform Template Function Implementation with std::invocable Concept and Execution Policy in C++, std::array and std::vector Type Arbitrary Nested Iterable Generator Functions Implementation in C++ and A Various Container Type Arbitrary Nested Iterable Generator Function Implementation in C++. Thanks to G. Sliepen's answer. In the parallel execution part, std::for_each()
structure is used instead of std::back_inserter()
usage. With Boost.Test tool, the transform operation for each element in nested std::deque
and nested std::vector
(nested level less than 16) is tested as below.
The experimental implementation
- Nested ranges comparison
In order to compare two std::ranges::input_range
things is equal or not, the operator ==
overloading implementation is as below.
// Equal operator for std::ranges::input_range
template<std::ranges::input_range Range1, std::ranges::input_range Range2>
bool operator==(const Range1& input1, const Range2& input2)
{
if (input1.size() != input2.size())
{
return false;
}
for (size_t i = 0; i < input1.size(); i++)
{
if (input1.at(i) != input2.at(i))
{
return false;
}
}
return true;
}
// Not equal operator for std::ranges::input_range
template<std::ranges::input_range Range1, std::ranges::input_range Range2>
bool operator!=(const Range1& input1, const Range2& input2)
{
if (input1.size() != input2.size())
{
return true;
}
for (size_t i = 0; i < input1.size(); i++)
{
if (input1.at(i) != input2.at(i))
{
return true;
}
}
return false;
}
Note: I've check this and the usage (test_result == expected_result)
as below should be compiled without any error. However, the error like binary '==': no operator found which takes a left-hand operand of type 'std::vector<std::vector<char,std::allocator<char>>,std::allocator<std::vector<char,std::allocator<char>>>>' (or there is no acceptable conversion)
occurred so that I write my own equal operator for std::ranges::input_range
. Maybe this is caused by the design of test case template with automated registration, but I am not quite sure.
- Nested
std::deque
test cases
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_0dimension, TestType, test_types)
{
constexpr size_t dim_num = 0;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_1dimension, TestType, test_types)
{
constexpr size_t dim_num = 1;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_2dimension, TestType, test_types)
{
constexpr size_t dim_num = 2;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_3dimension, TestType, test_types)
{
constexpr size_t dim_num = 3;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_4dimension, TestType, test_types)
{
constexpr size_t dim_num = 4;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_5dimension, TestType, test_types)
{
constexpr size_t dim_num = 5;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_6dimension, TestType, test_types)
{
constexpr size_t dim_num = 6;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_7dimension, TestType, test_types)
{
constexpr size_t dim_num = 7;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_8dimension, TestType, test_types)
{
constexpr size_t dim_num = 8;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_9dimension, TestType, test_types)
{
constexpr size_t dim_num = 9;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_10dimension, TestType, test_types)
{
constexpr size_t dim_num = 10;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_11dimension, TestType, test_types)
{
constexpr size_t dim_num = 11;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_12dimension, TestType, test_types)
{
constexpr size_t dim_num = 12;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_13dimension, TestType, test_types)
{
constexpr size_t dim_num = 13;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_14dimension, TestType, test_types)
{
constexpr size_t dim_num = 14;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(deque_lambda_with_auto_15dimension, TestType, test_types)
{
constexpr size_t dim_num = 15;
auto test_object = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_container_generator<dim_num, std::deque, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
- Nested
std::vector
test cases
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_0dimension, TestType, test_types)
{
constexpr size_t dim_num = 0;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_1dimension, TestType, test_types)
{
constexpr size_t dim_num = 1;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_2dimension, TestType, test_types)
{
constexpr size_t dim_num = 2;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_3dimension, TestType, test_types)
{
constexpr size_t dim_num = 3;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_4dimension, TestType, test_types)
{
constexpr size_t dim_num = 4;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_5dimension, TestType, test_types)
{
constexpr size_t dim_num = 5;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_6dimension, TestType, test_types)
{
constexpr size_t dim_num = 6;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_7dimension, TestType, test_types)
{
constexpr size_t dim_num = 7;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_8dimension, TestType, test_types)
{
constexpr size_t dim_num = 8;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_9dimension, TestType, test_types)
{
constexpr size_t dim_num = 9;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_10dimension, TestType, test_types)
{
constexpr size_t dim_num = 10;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_11dimension, TestType, test_types)
{
constexpr size_t dim_num = 11;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_12dimension, TestType, test_types)
{
constexpr size_t dim_num = 12;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_13dimension, TestType, test_types)
{
constexpr size_t dim_num = 13;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_14dimension, TestType, test_types)
{
constexpr size_t dim_num = 14;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_15dimension, TestType, test_types)
{
constexpr size_t dim_num = 15;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
Full Testing Code
The full testing code:
#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <complex>
#include <concepts>
#include <deque>
#include <execution>
#include <exception>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <mutex>
#include <numeric>
#include <optional>
#include <stdexcept>
#include <string>
#include <thread>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
#include <boost/utility.hpp>
//#define USE_BOOST_MULTIDIMENSIONAL_ARRAY
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
#include <boost/multi_array.hpp>
#include <boost/multi_array/algorithm.hpp>
#include <boost/multi_array/base.hpp>
#include <boost/multi_array/collection_concept.hpp>
#endif
#define BOOST_TEST_MODULE tests_for_recursive_transform
#include <boost/test/included/unit_test.hpp>
#include <boost/test/test_tools.hpp>
#include <boost/mpl/list.hpp>
template<typename T>
concept is_inserterable = requires(T x)
{
std::inserter(x, std::ranges::end(x));
};
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<typename T>
concept is_multi_array = requires(T x)
{
x.num_dimensions();
x.shape();
boost::multi_array(x);
};
#endif
template<std::ranges::input_range Range>
Range recursive_print(const Range& input, const int level = 0)
{
Range output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
std::transform(input.cbegin(), input.cend(), output.begin(),
[level](auto& x)
{
std::cout << std::string(level, ' ') << x << std::endl;
return x;
}
);
return output;
}
template<std::ranges::input_range Range>
requires std::ranges::input_range<std::ranges::range_value_t<Range>>
Range recursive_print(const Range& input, const int level = 0)
{
Range output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
std::transform(input.cbegin(), input.cend(), output.begin(),
[level](auto& element)
{
return recursive_print(element, level + 1);
}
);
return output;
}
// recursive_transform implementation
template<class T, class F>
constexpr auto recursive_transform(const T& input, const F& f)
{
return f(input);
}
// specific case for std::array
template<class T, std::size_t S, class F>
constexpr auto recursive_transform(const std::array<T, S>& input, const F& f)
{
using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));
std::array<TransformedValueType, S> output;
std::transform(input.cbegin(), input.cend(), output.begin(),
[&f](auto&& element)
{
return recursive_transform(element, f);
}
);
return output;
}
template<template<class...> class Container, class Function, class... Ts>
requires (is_inserterable<Container<Ts...>> && !std::invocable<Function, Container<Ts...>>)
constexpr auto recursive_transform(const Container<Ts...>& input, const Function& f)
{
using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));
Container<TransformedValueType> output;
std::transform(input.cbegin(), input.cend(), std::inserter(output, std::ranges::end(output)),
[&](auto&& element)
{
return recursive_transform(element, f);
}
);
return output;
}
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<is_multi_array T, class F>
requires(!std::invocable<F, T>)
constexpr auto recursive_transform(const T& input, const F& f)
{
boost::multi_array output(input);
for (decltype(+input.shape()[0]) i{}; i != input.shape()[0]; ++i)
{
output[i] = recursive_transform(input[i], f);
}
return output;
}
#endif
// recursive_transform implementation (with execution policy)
template<class ExPo, class T, class F>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_transform(ExPo execution_policy, const T& input, const F& f)
{
return f(input);
}
// specific case for std::array
template<class ExPo, class T, std::size_t S, class F>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_transform(ExPo execution_policy, const std::array<T, S>& input, const F& f)
{
using TransformedValueType = decltype(recursive_transform(execution_policy, *input.cbegin(), f));
std::array<TransformedValueType, S> output;
std::transform(input.cbegin(), input.cend(), output.begin(),
[execution_policy, &f](auto&& element)
{
return recursive_transform(execution_policy, element, f);
}
);
return output;
}
template<class ExPo, template<class...> class Container, class Function, class... Ts>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (is_inserterable<Container<Ts...>> && !std::invocable<Function, Container<Ts...>>)
constexpr auto recursive_transform(ExPo execution_policy, const Container<Ts...>& input, const Function& f)
{
using TransformedValueType = decltype(recursive_transform(execution_policy, *input.cbegin(), f));
Container<TransformedValueType> output(input.size());
std::mutex mutex;
std::for_each(execution_policy, input.cbegin(), input.cend(),
[&](auto&& element)
{
auto result = recursive_transform(execution_policy, element, f);
std::lock_guard lock(mutex);
output.emplace_back(std::move(result));
}
);
return output;
}
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<class ExPo, is_multi_array T, class F>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (!std::invocable<F, T>)
constexpr auto recursive_transform(const T& input, const F& f)
{
boost::multi_array output(input);
for (decltype(+input.shape()[0]) i{}; i != input.shape()[0]; ++i)
{
output[i] = recursive_transform(execution_policy, input[i], f);
}
return output;
}
#endif
template<std::size_t dim, class T>
constexpr auto n_dim_vector_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_vector_generator<dim - 1>(input, times);
std::vector<decltype(element)> output(times, element);
return output;
}
}
template<std::size_t dim, std::size_t times, class T>
constexpr auto n_dim_array_generator(T input)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_array_generator<dim - 1, times>(input);
std::array<decltype(element), times> output;
std::fill(std::begin(output), std::end(output), element);
return output;
}
}
template<std::size_t dim, class T>
constexpr auto n_dim_deque_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_deque_generator<dim - 1>(input, times);
std::deque<decltype(element)> output(times, element);
return output;
}
}
template<std::size_t dim, class T>
constexpr auto n_dim_list_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_list_generator<dim - 1>(input, times);
std::list<decltype(element)> output(times, element);
return output;
}
}
template<std::size_t dim, template<class...> class Container = std::vector, class T>
constexpr auto n_dim_container_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
return Container(times, n_dim_container_generator<dim - 1, Container, T>(input, times));
}
}
template<typename T>
struct recursive_iter_value_t_detail
{
using type = T;
};
template <std::ranges::range T>
struct recursive_iter_value_t_detail<T>
: recursive_iter_value_t_detail<std::iter_value_t<T>>
{ };
template<typename T>
using recursive_iter_value_t = typename recursive_iter_value_t_detail<T>::type;
// Equal operator for std::ranges::input_range
template<std::ranges::input_range Range1, std::ranges::input_range Range2>
bool operator==(const Range1& input1, const Range2& input2)
{
if (input1.size() != input2.size())
{
return false;
}
for (size_t i = 0; i < input1.size(); i++)
{
if (input1.at(i) != input2.at(i))
{
return false;
}
}
return true;
}
// Not equal operator for std::ranges::input_range
template<std::ranges::input_range Range1, std::ranges::input_range Range2>
bool operator!=(const Range1& input1, const Range2& input2)
{
if (input1.size() != input2.size())
{
return true;
}
for (size_t i = 0; i < input1.size(); i++)
{
if (input1.at(i) != input2.at(i))
{
return true;
}
}
return false;
}
typedef boost::mpl::list<char, int, short, long, long long int, unsigned char, unsigned int, unsigned short int, unsigned long int, float, double, long double> test_types;
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_0dimension, TestType, test_types)
{
constexpr size_t dim_num = 0;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_1dimension, TestType, test_types)
{
constexpr size_t dim_num = 1;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_2dimension, TestType, test_types)
{
constexpr size_t dim_num = 2;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_3dimension, TestType, test_types)
{
constexpr size_t dim_num = 3;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_4dimension, TestType, test_types)
{
constexpr size_t dim_num = 4;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_5dimension, TestType, test_types)
{
constexpr size_t dim_num = 5;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_6dimension, TestType, test_types)
{
constexpr size_t dim_num = 6;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_7dimension, TestType, test_types)
{
constexpr size_t dim_num = 7;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_8dimension, TestType, test_types)
{
constexpr size_t dim_num = 8;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_9dimension, TestType, test_types)
{
constexpr size_t dim_num = 9;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_10dimension, TestType, test_types)
{
constexpr size_t dim_num = 10;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_11dimension, TestType, test_types)
{
constexpr size_t dim_num = 11;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_12dimension, TestType, test_types)
{
constexpr size_t dim_num = 12;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_13dimension, TestType, test_types)
{
constexpr size_t dim_num = 13;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_14dimension, TestType, test_types)
{
constexpr size_t dim_num = 14;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(vector_lambda_with_auto_15dimension, TestType, test_types)
{
constexpr size_t dim_num = 15;
auto test_object = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2), 3);
auto test_result = recursive_transform(std::execution::par, test_object, [](auto& element) { return element * 2; });
auto expected_result = n_dim_vector_generator<dim_num, TestType>(static_cast<TestType>(2) * 2, 3);
// Content comparison
if (test_result == expected_result)
{
BOOST_TEST(true);
}
else
{
BOOST_TEST(false);
}
}
If the enough memory and compile time / run time resource is available, the test output is like:
Running 192 test cases...
*** No errors detected
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A recursive_transform Template Function Implementation with
std::invocable
concept in C++,A recursive_transform Template Function with Execution Policy,
std::array and std::vector Type Arbitrary Nested Iterable Generator Functions Implementation in C++ and
A Various Container Type Arbitrary Nested Iterable Generator Function Implementation in C++
What changes has been made in the code since last question?
I am trying to design some unit test cases as complete as possible in various usage scenarios.
Why a new review is being asked for?
About the part of Nested ranges comparison, I am not sure if it is a good idea to compare each element in two nested ranges in the way above. If there is any possible improvement, please let me know.