This is a follow-up question for A recursive_reduce Template Function with Unwrap Level Implementation in C++ and A recursive_reduce_string Template Function Implementation in C++. Considering the issue mentioned in G. Sliepen's answer, some modifications are made here:
It is not generic enough. Even if you only wanted it to be limited to strings, consider that
std::string
is just a type alias forstd::basic_string<char>
. What aboutstd::wstring
,std::u8string
,std::pmr::string
,std::string_view
, C strings and all other kinds of strings?The name implies it is similar to
std::reduce()
, however it doesn't support a custom reduction operator, doesn't support an initial element, and doesn't support parallelism.
In this post, the updated recursive_reduce_string
template function not only support various type of string (std::string
, std::wstring
, std::u8string
, std::pmr::string
) but also support custom reduction operator, initial element and parallelism.
The experimental implementation
recursive_reduce_string
template function implementation// recursive_reduce_string template function template<std::size_t base_level, class T> constexpr auto recursive_reduce_string(const T& input) { if (input.empty()) { throw std::runtime_error("input is empty!"); } if constexpr (recursive_depth<T>() == base_level) { return input; } else if constexpr (recursive_depth<T>() == base_level + 1) { auto output = input[0]; for(int i = 1; i < std::ranges::size(input); ++i) { output+=input[i]; } return output; } else { auto result = recursive_reduce_string<base_level>( UL::recursive_transform<recursive_depth<T>() - 2>( input, [](auto&& element){ return recursive_reduce_string<base_level>(element); }) ); return result; } } // recursive_reduce_string template function (with execution policy) template<std::size_t base_level, class ExPo, class T> requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) constexpr auto recursive_reduce_string(ExPo execution_policy, const T& input) { if (input.empty()) { throw std::runtime_error("input is empty!"); } if constexpr (recursive_depth<T>() == base_level) { return input; } else if constexpr (recursive_depth<T>() == base_level + 1) { auto output = input[0]; for(int i = 1; i < std::ranges::size(input); ++i) { output+=input[i]; } return output; } else { auto result = recursive_reduce_string<base_level>( execution_policy, UL::recursive_transform<recursive_depth<T>() - 2>( execution_policy, input, [&](auto&& element){ return recursive_reduce_string<base_level>(execution_policy, element); }) ); return result; } } // recursive_reduce_string template function with initial value template<std::size_t base_level, class T, class TI> requires (is_summable<recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>, TI>) constexpr auto recursive_reduce_string(const T& input, const TI init) { if (input.empty()) { throw std::runtime_error("input is empty!"); } if constexpr (recursive_depth<T>() == base_level) { return input; } else if constexpr (recursive_depth<T>() == base_level + 1) { auto output = init; for(auto&& element : input) { output+=element; } return output; } else { auto result = recursive_reduce_string<base_level>( UL::recursive_transform<recursive_depth<T>() - 2>( input, [&](auto&& element){ return recursive_reduce_string<base_level>(element); }), init ); return result; } } // recursive_reduce_string template function with execution policy and initial value template<std::size_t base_level, class ExPo, class T, class TI> requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> && is_summable<recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>, TI>) constexpr auto recursive_reduce_string(ExPo execution_policy, const T& input, const TI init) { if (input.empty()) { throw std::runtime_error("input is empty!"); } if constexpr (recursive_depth<T>() == base_level) { return input; } else if constexpr (recursive_depth<T>() == base_level + 1) { auto output = init; for(auto&& element : input) { output+=element; } return output; } else { auto result = recursive_reduce_string<base_level>( execution_policy, UL::recursive_transform<recursive_depth<T>() - 2>( execution_policy, input, [&](auto&& element){ return recursive_reduce_string<base_level>(execution_policy, element); }), init ); return result; } } // recursive_reduce_string template function with initial value and specified operation template<std::size_t base_level, class T, class TI, class BinaryOp> requires ( std::regular_invocable< BinaryOp, recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>, recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>>) constexpr auto recursive_reduce_string(const T& input, const TI init, BinaryOp binary_op) { if (input.empty()) { throw std::runtime_error("input is empty!"); } if constexpr (recursive_depth<T>() == base_level) { return input; } else if constexpr (recursive_depth<T>() == base_level + 1) { auto output = init; for(auto&& element : input) { output = std::invoke(binary_op, output, element); } return output; } else { auto result = recursive_reduce_string<base_level>( UL::recursive_transform<recursive_depth<T>() - 2>( input, [&](auto&& element){ return recursive_reduce_string<base_level>(element); }), init, binary_op ); return result; } } // recursive_reduce_string template function with execution policy, initial value and specified operation template<std::size_t base_level, class ExPo, class T, class TI, class BinaryOp> requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> && std::regular_invocable< BinaryOp, recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>, recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>>) constexpr auto recursive_reduce_string(ExPo execution_policy, const T& input, const TI init, BinaryOp binary_op) { if (input.empty()) { throw std::runtime_error("input is empty!"); } if constexpr (recursive_depth<T>() == base_level) { return input; } else if constexpr (recursive_depth<T>() == base_level + 1) { auto output = init; for(auto&& element : input) { output = std::invoke(binary_op, output, element); } return output; } else { auto result = recursive_reduce_string<base_level>( execution_policy, UL::recursive_transform<recursive_depth<T>() - 2>( execution_policy, input, [&](auto&& element){ return recursive_reduce_string<base_level>(execution_policy, element); } ), init, binary_op ); return result; } }
Full Testing Code
The full testing code:
#include <algorithm>
#include <array>
#include <chrono>
#include <concepts>
#include <deque>
#include <execution>
#include <experimental/array>
#include <iostream>
#include <queue>
#include <ranges>
#include <string>
#include <vector>
// is_reservable concept
template<class T>
concept is_reservable = requires(T input)
{
input.reserve(1);
};
// is_sized concept, https://codereview.stackexchange.com/a/283581/231235
template<class T>
concept is_sized = requires(T x)
{
std::size(x);
};
template<class T1, class T2>
concept is_summable = requires(T1 x1, T2 x2) { x1 + x2; };
// has_arithmetic_operations concept
template<class T>
concept has_arithmetic_operations = requires(T input)
{
std::plus<>{}(input, input);
std::minus<>{}(input, input);
std::multiplies<>{}(input, input);
std::divides<>{}(input, input);
};
// recursive_depth function implementation
template<typename T>
constexpr std::size_t recursive_depth()
{
return std::size_t{0};
}
template<std::ranges::input_range Range>
constexpr std::size_t recursive_depth()
{
return recursive_depth<std::ranges::range_value_t<Range>>() + std::size_t{1};
}
// recursive_variadic_invoke_result_t implementation
template<std::size_t, typename, typename, typename...>
struct recursive_variadic_invoke_result { };
template<typename F, class...Ts1, template<class...>class Container1, typename... Ts>
struct recursive_variadic_invoke_result<1, F, Container1<Ts1...>, Ts...>
{
using type = Container1<std::invoke_result_t<F,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...>>;
};
template<std::size_t unwrap_level, typename F, class...Ts1, template<class...>class Container1, typename... Ts>
requires ( std::ranges::input_range<Container1<Ts1...>> &&
requires { typename recursive_variadic_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...>::type; }) // The rest arguments are ranges
struct recursive_variadic_invoke_result<unwrap_level, F, Container1<Ts1...>, Ts...>
{
using type = Container1<
typename recursive_variadic_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...
>::type>;
};
template<std::size_t unwrap_level, typename F, typename T1, typename... Ts>
using recursive_variadic_invoke_result_t = typename recursive_variadic_invoke_result<unwrap_level, F, T1, Ts...>::type;
// recursive_array_invoke_result implementation
template<std::size_t, typename, typename, typename...>
struct recursive_array_invoke_result { };
template< typename F,
template<class, std::size_t> class Container,
typename T,
std::size_t N>
struct recursive_array_invoke_result<1, F, Container<T, N>>
{
using type = Container<
std::invoke_result_t<F, std::ranges::range_value_t<Container<T, N>>>,
N>;
};
template< std::size_t unwrap_level,
typename F,
template<class, std::size_t> class Container,
typename T,
std::size_t N>
requires ( std::ranges::input_range<Container<T, N>> &&
requires { typename recursive_array_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container<T, N>>>::type; }) // The rest arguments are ranges
struct recursive_array_invoke_result<unwrap_level, F, Container<T, N>>
{
using type = Container<
typename recursive_array_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container<T, N>>
>::type, N>;
};
template< std::size_t unwrap_level,
typename F,
template<class, std::size_t> class Container,
typename T,
std::size_t N>
using recursive_array_invoke_result_t = typename recursive_array_invoke_result<unwrap_level, F, Container<T, N>>::type;
// recursive_unwrap_type_t struct implementation, https://codereview.stackexchange.com/q/284610/231235
template<std::size_t, typename, typename...>
struct recursive_unwrap_type { };
template<class...Ts1, template<class...>class Container1, typename... Ts>
struct recursive_unwrap_type<1, Container1<Ts1...>, Ts...>
{
using type = std::ranges::range_value_t<Container1<Ts1...>>;
};
template<std::size_t unwrap_level, class...Ts1, template<class...>class Container1, typename... Ts>
requires ( std::ranges::input_range<Container1<Ts1...>> &&
requires { typename recursive_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...>::type; }) // The rest arguments are ranges
struct recursive_unwrap_type<unwrap_level, Container1<Ts1...>, Ts...>
{
using type = typename recursive_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container1<Ts1...>>
>::type;
};
template<std::size_t unwrap_level, typename T1, typename... Ts>
using recursive_unwrap_type_t = typename recursive_unwrap_type<unwrap_level, T1, Ts...>::type;
// recursive_array_unwrap_type struct implementation, https://stackoverflow.com/a/76347485/6667035
template<std::size_t, typename>
struct recursive_array_unwrap_type { };
template<template<class, std::size_t> class Container,
typename T,
std::size_t N>
struct recursive_array_unwrap_type<1, Container<T, N>>
{
using type = std::ranges::range_value_t<Container<T, N>>;
};
template<std::size_t unwrap_level, template<class, std::size_t> class Container,
typename T,
std::size_t N>
requires ( std::ranges::input_range<Container<T, N>> &&
requires { typename recursive_array_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container<T, N>>>::type; }) // The rest arguments are ranges
struct recursive_array_unwrap_type<unwrap_level, Container<T, N>>
{
using type = typename recursive_array_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container<T, N>>
>::type;
};
template<std::size_t unwrap_level, class Container>
using recursive_array_unwrap_type_t = typename recursive_array_unwrap_type<unwrap_level, Container>::type;
// https://codereview.stackexchange.com/a/253039/231235
template<template<class...> class Container = std::vector, std::size_t dim, 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<Container, dim - 1, T>(input, times));
}
}
namespace UL // unwrap_level
{
template< std::ranges::input_range Container,
std::copy_constructible F>
requires (std::ranges::view<Container>&&
std::is_object_v<F>)
constexpr auto make_view(const Container& input, const F& f) noexcept
{
return std::ranges::transform_view(
input,
[&f](const auto&& element) constexpr { return recursive_transform(element, f ); } );
}
/* Override make_view to catch dangling references. A borrowed range is
* safe from dangling..
*/
template <std::ranges::input_range T>
requires (!std::ranges::borrowed_range<T>)
constexpr std::ranges::dangling make_view(T&&) noexcept
{
return std::ranges::dangling();
}
// clone_empty_container template function implementation
template< std::size_t unwrap_level = 1,
std::ranges::input_range Container,
std::copy_constructible F>
requires (std::ranges::view<Container>&&
std::is_object_v<F>)
constexpr auto clone_empty_container(const Container& input, const F& f) noexcept
{
const auto view = make_view(input, f);
recursive_variadic_invoke_result<unwrap_level, F, Container> output(std::span{input});
return output;
}
// recursive_transform template function implementation (the version with unwrap_level template parameter)
template< std::size_t unwrap_level = 1,
class T,
std::copy_constructible F,
class Proj = std::identity>
requires (unwrap_level <= recursive_depth<T>()&& // handling incorrect unwrap levels more gracefully, https://codereview.stackexchange.com/a/283563/231235
std::ranges::view<T>&&
std::is_object_v<F>)
constexpr auto recursive_transform(const T& input, const F& f, Proj proj = {} )
{
if constexpr (unwrap_level > 0)
{
auto output = clone_empty_container(input, f);
if constexpr (is_reservable<decltype(output)>&&
is_sized<decltype(input)>)
{
output.reserve(input.size());
std::ranges::transform(
input,
std::ranges::begin(output),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); },
proj
);
}
else
{
std::ranges::transform(
input,
std::inserter(output, std::ranges::end(output)),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); },
proj
);
}
return output;
}
else if constexpr(std::regular_invocable<F, T>)
{
return std::invoke(f, std::invoke(proj, input));
}
else
{
static_assert(!std::regular_invocable<F, T>, "Uninvocable?");
}
}
/* This overload of recursive_transform is to support std::array
*/
template< std::size_t unwrap_level = 1,
template<class, std::size_t> class Container,
typename T,
std::size_t N,
typename F >
requires (std::ranges::input_range<Container<T, N>>)
constexpr auto recursive_transform(const Container<T, N>& input, const F& f)
{
recursive_array_invoke_result_t<unwrap_level, F, Container, T, N> output{};
if constexpr (unwrap_level > 1)
{
std::ranges::transform(
input,
std::ranges::begin(output),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); }
);
}
else
{
std::ranges::transform(
input,
std::ranges::begin(output),
f
);
}
return output;
}
// recursive_transform function implementation (the version with unwrap_level, without using view)
template<std::size_t unwrap_level = 1, class T, class F>
requires (unwrap_level <= recursive_depth<T>()&& // handling incorrect unwrap levels more gracefully, https://codereview.stackexchange.com/a/283563/231235
!std::ranges::view<T>)
constexpr auto recursive_transform(const T& input, const F& f)
{
if constexpr (unwrap_level > 0)
{
recursive_variadic_invoke_result_t<unwrap_level, F, T> output{};
std::ranges::transform(
input, // passing a range to std::ranges::transform()
std::inserter(output, std::ranges::end(output)),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); }
);
return output;
}
else
{
return std::invoke(f, input); // use std::invoke()
}
}
// recursive_transform implementation (the version with unwrap_level, with execution policy)
template<std::size_t unwrap_level = 1, class ExPo, class T, std::copy_constructible F>
requires (unwrap_level <= recursive_depth<T>() && // handling incorrect unwrap levels more gracefully, https://codereview.stackexchange.com/a/283563/231235
std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_transform(ExPo execution_policy, const T& input, const F& f)
{
if constexpr (unwrap_level > 0)
{
recursive_variadic_invoke_result_t<unwrap_level, F, T> output{};
output.resize(input.size());
std::mutex mutex;
std::transform(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
[&](auto&& element)
{
std::lock_guard lock(mutex);
return recursive_transform<unwrap_level - 1>(execution_policy, element, f);
});
return output;
}
else
{
return std::invoke(f, input);
}
}
// recursive_transform implementation (binary case, the version with unwrap_level)
template<std::size_t unwrap_level = 1, class ExPo, std::ranges::input_range R1, std::ranges::input_range R2, std::copy_constructible F>
constexpr auto recursive_transform(ExPo execution_policy, const R1& input1, const R2& input2, const F& f)
{
if constexpr (unwrap_level > 0)
{
recursive_variadic_invoke_result_t<unwrap_level, F, R1> output{};
output.resize(input1.size());
std::mutex mutex;
std::transform(execution_policy, std::ranges::cbegin(input1), std::ranges::cend(input1), std::ranges::cbegin(input2), std::ranges::begin(output),
[&](auto&& element1, auto&& element2)
{
std::lock_guard lock(mutex);
return recursive_transform<unwrap_level - 1>(execution_policy, element1, element2, f);
});
return output;
}
else
{
return std::invoke(f, input1, input2);
}
}
}
// recursive_reduce_string template function
template<std::size_t base_level, class T>
constexpr auto recursive_reduce_string(const T& input)
{
if (input.empty())
{
throw std::runtime_error("input is empty!");
}
if constexpr (recursive_depth<T>() == base_level)
{
return input;
}
else if constexpr (recursive_depth<T>() == base_level + 1)
{
auto output = input[0];
for(int i = 1; i < std::ranges::size(input); ++i)
{
output+=input[i];
}
return output;
}
else
{
auto result = recursive_reduce_string<base_level>(
UL::recursive_transform<recursive_depth<T>() - 2>(
input,
[](auto&& element){ return recursive_reduce_string<base_level>(element); })
);
return result;
}
}
// recursive_reduce_string template function (with execution policy)
template<std::size_t base_level, class ExPo, class T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_reduce_string(ExPo execution_policy, const T& input)
{
if (input.empty())
{
throw std::runtime_error("input is empty!");
}
if constexpr (recursive_depth<T>() == base_level)
{
return input;
}
else if constexpr (recursive_depth<T>() == base_level + 1)
{
auto output = input[0];
for(int i = 1; i < std::ranges::size(input); ++i)
{
output+=input[i];
}
return output;
}
else
{
auto result = recursive_reduce_string<base_level>(
execution_policy,
UL::recursive_transform<recursive_depth<T>() - 2>(
execution_policy,
input,
[&](auto&& element){ return recursive_reduce_string<base_level>(execution_policy, element); })
);
return result;
}
}
// recursive_reduce_string template function with initial value
template<std::size_t base_level, class T, class TI>
requires (is_summable<recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>, TI>)
constexpr auto recursive_reduce_string(const T& input, const TI init)
{
if (input.empty())
{
throw std::runtime_error("input is empty!");
}
if constexpr (recursive_depth<T>() == base_level)
{
return input;
}
else if constexpr (recursive_depth<T>() == base_level + 1)
{
auto output = init;
for(auto&& element : input)
{
output+=element;
}
return output;
}
else
{
auto result = recursive_reduce_string<base_level>(
UL::recursive_transform<recursive_depth<T>() - 2>(
input,
[&](auto&& element){ return recursive_reduce_string<base_level>(element); }),
init
);
return result;
}
}
// recursive_reduce_string template function with execution policy and initial value
template<std::size_t base_level, class ExPo, class T, class TI>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
is_summable<recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>, TI>)
constexpr auto recursive_reduce_string(ExPo execution_policy, const T& input, const TI init)
{
if (input.empty())
{
throw std::runtime_error("input is empty!");
}
if constexpr (recursive_depth<T>() == base_level)
{
return input;
}
else if constexpr (recursive_depth<T>() == base_level + 1)
{
auto output = init;
for(auto&& element : input)
{
output+=element;
}
return output;
}
else
{
auto result = recursive_reduce_string<base_level>(
execution_policy,
UL::recursive_transform<recursive_depth<T>() - 2>(
execution_policy,
input,
[&](auto&& element){ return recursive_reduce_string<base_level>(execution_policy, element); }),
init
);
return result;
}
}
// recursive_reduce_string template function with initial value and specified operation
template<std::size_t base_level, class T, class TI, class BinaryOp>
requires ( std::regular_invocable<
BinaryOp,
recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>,
recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>>)
constexpr auto recursive_reduce_string(const T& input, const TI init, BinaryOp binary_op)
{
if (input.empty())
{
throw std::runtime_error("input is empty!");
}
if constexpr (recursive_depth<T>() == base_level)
{
return input;
}
else if constexpr (recursive_depth<T>() == base_level + 1)
{
auto output = init;
for(auto&& element : input)
{
output = std::invoke(binary_op, output, element);
}
return output;
}
else
{
auto result = recursive_reduce_string<base_level>(
UL::recursive_transform<recursive_depth<T>() - 2>(
input,
[&](auto&& element){ return recursive_reduce_string<base_level>(element); }),
init,
binary_op
);
return result;
}
}
// recursive_reduce_string template function with execution policy, initial value and specified operation
template<std::size_t base_level, class ExPo, class T, class TI, class BinaryOp>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
std::regular_invocable<
BinaryOp,
recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>,
recursive_unwrap_type_t<recursive_depth<T>() - base_level, T>>)
constexpr auto recursive_reduce_string(ExPo execution_policy, const T& input, const TI init, BinaryOp binary_op)
{
if (input.empty())
{
throw std::runtime_error("input is empty!");
}
if constexpr (recursive_depth<T>() == base_level)
{
return input;
}
else if constexpr (recursive_depth<T>() == base_level + 1)
{
auto output = init;
for(auto&& element : input)
{
output = std::invoke(binary_op, output, element);
}
return output;
}
else
{
auto result = recursive_reduce_string<base_level>(
execution_policy,
UL::recursive_transform<recursive_depth<T>() - 2>(
execution_policy,
input,
[&](auto&& element){ return recursive_reduce_string<base_level>(execution_policy, element); }
),
init,
binary_op
);
return result;
}
}
template<class T>
requires (std::ranges::input_range<T>)
constexpr auto recursive_print(const T& input, const int level = 0)
{
T output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":" << "\n";
std::transform(input.cbegin(), input.cend(), output.begin(),
[level](auto&& x)
{
std::cout << std::string(level, ' ') << x << "\n";
return x;
}
);
return output;
}
template<class T>
requires (std::ranges::input_range<T> &&
std::ranges::input_range<std::ranges::range_value_t<T>>)
constexpr T recursive_print(const T& input, const int level = 0)
{
T output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":" << "\n";
std::transform(input.cbegin(), input.cend(), output.begin(),
[level](auto&& element)
{
return recursive_print(element, level + 1);
}
);
return output;
}
void recursive_reduce_string_tests()
{
std::cout << "recursive_reduce_string function test with std::vector<std::string>:\n";
std::vector<std::string> word_vector1 = {"foo", "bar", "baz", "quux"};
std::cout << recursive_reduce_string<1>(word_vector1) << '\n';
std::cout << "recursive_reduce_string function test with std::vector<std::vector<std::string>>:\n";
std::vector<std::vector<std::string>> word_vector2 = {word_vector1, word_vector1, word_vector1};
std::cout << recursive_reduce_string<1>(word_vector2) << "\n\n";
std::cout << "recursive_reduce_string function test with std::array<std::string, 3>:\n";
std::array<std::string, 3> word_array1 = {"foo", "bar", "baz"};
std::cout << recursive_reduce_string<1>(word_array1) << '\n';
std::cout << "recursive_reduce_string function test with std::array<std::array<std::string, 3>, 3>:\n";
std::array<decltype(word_array1), 3> word_array2 = {word_array1, word_array1, word_array1};
std::cout << recursive_reduce_string<1>(word_array2) << "\n\n";
std::cout << "recursive_reduce_string function test with execution policy, std::vector<std::string>:\n";
std::cout << recursive_reduce_string<1>(std::execution::seq, word_vector1) << '\n';
std::cout << "recursive_reduce_string function test with execution policy, std::vector<std::vector<std::string>>:\n";
std::cout << recursive_reduce_string<1>(std::execution::seq, word_vector2) << "\n\n";
std::cout << "recursive_reduce_string function test with initial value, std::vector<std::string>:\n";
std::string s1{ "_" };
std::cout << recursive_reduce_string<1>(word_vector1, s1) << '\n';
std::cout << "recursive_reduce_string function test with initial value, std::vector<std::vector<std::string>>:\n";
std::cout << recursive_reduce_string<1>(word_vector2, s1) << "\n\n";
std::cout << "recursive_reduce_string function test with execution policy, initial value, std::vector<std::string>:\n";
std::cout << recursive_reduce_string<1>(std::execution::seq, word_vector1, s1) << '\n';
std::cout << "recursive_reduce_string function test with execution policy, initial value, std::vector<std::vector<std::string>>:\n";
std::cout << recursive_reduce_string<1>(std::execution::seq, word_vector2, s1) << "\n\n";
std::cout << "recursive_reduce_string function test with initial value, specified operation, std::vector<std::string>:\n";
std::cout << recursive_reduce_string<1>(
word_vector1,
s1,
[](std::string x, std::string y) { return x + " " + y; }) << "\n\n";
std::cout << "recursive_reduce_string function test with execution policy, initial value, specified operation, std::vector<std::string>:\n";
std::cout << recursive_reduce_string<1>(
std::execution::seq,
word_vector1,
s1,
[](std::string x, std::string y) { return x + " " + y; }) << "\n\n";
std::cout << "recursive_reduce_string function test with std::deque<std::string>:\n";
std::deque<std::string> word_deque1 = {"1", "2", "3", "4"};
std::cout << recursive_reduce_string<1>(word_deque1) << '\n';
std::cout << "recursive_reduce_string function test with std::deque<std::deque<std::string>>:\n";
std::deque<std::deque<std::string>> word_deque2 = {word_deque1, word_deque1, word_deque1};
std::cout << recursive_reduce_string<1>(word_deque2) << "\n\n";
std::cout << "recursive_reduce_string function test with std::vector<std::wstring>:\n";
std::vector<std::wstring> wstring_vector1{};
for(int i = 0; i < 4; ++i)
{
wstring_vector1.push_back(std::to_wstring(i));
}
std::wcout << recursive_reduce_string<1>(wstring_vector1) << '\n';
std::cout << "recursive_reduce_string function test with std::vector<std::vector<std::wstring>>:\n";
std::vector<decltype(wstring_vector1)> wstring_vector2{};
for(int i = 0; i < 4; ++i)
{
wstring_vector2.push_back(wstring_vector1);
}
std::wcout << recursive_reduce_string<1>(wstring_vector2) << "\n\n";
std::cout << "recursive_reduce_string function test with std::array<std::wstring, 4>:\n";
auto wstring_array1 = std::experimental::make_array(
std::to_wstring(1),
std::to_wstring(2),
std::to_wstring(3),
std::to_wstring(4)
);
std::wcout << recursive_reduce_string<1>(wstring_array1) << '\n';
std::cout << "recursive_reduce_string function test with std::array<std::array<std::wstring, 4>, 4>:\n";
auto wstring_array2 = std::experimental::make_array(
wstring_array1,
wstring_array1,
wstring_array1,
wstring_array1
);
std::wcout << recursive_reduce_string<1>(wstring_array2) << "\n\n";
std::cout << "recursive_reduce_string function test with std::deque<std::wstring>:\n";
std::deque<std::wstring> wstring_deque1{};
for(int i = 0; i < 4; ++i)
{
wstring_deque1.push_back(std::to_wstring(i));
}
std::wcout << recursive_reduce_string<1>(wstring_vector1) << '\n';
std::cout << "recursive_reduce_string function test with std::deque<std::deque<std::wstring>>:\n";
std::deque<decltype(wstring_deque1)> wstring_deque2{};
for(int i = 0; i < 4; ++i)
{
wstring_deque2.push_back(wstring_deque1);
}
std::wcout << recursive_reduce_string<1>(wstring_deque2) << "\n\n";
std::cout << "recursive_reduce_string function test with std::vector<std::u8string>:\n";
std::vector<std::u8string> u8string_vector1{};
for(int i = 0; i < 4; ++i)
{
u8string_vector1.push_back(u8"\u20AC2.00");
}
std::cout << reinterpret_cast<const char*>(recursive_reduce_string<1>(u8string_vector1).c_str()) << '\n';
std::cout << "recursive_reduce_string function test with std::vector<std::vector<std::u8string>>:\n";
std::vector<std::vector<std::u8string>> u8string_vector2 = {u8string_vector1, u8string_vector1, u8string_vector1};
std::cout << reinterpret_cast<const char*>(recursive_reduce_string<1>(u8string_vector2).c_str()) << "\n\n";
std::cout << "recursive_reduce_string function test with std::vector<std::pmr::string>:\n";
std::pmr::string pmr_string1 = "123";
std::vector<std::pmr::string> pmr_string_vector1 = {pmr_string1, pmr_string1, pmr_string1};
std::cout << recursive_reduce_string<1>(pmr_string_vector1) << "\n\n";
return;
}
int main()
{
auto start = std::chrono::system_clock::now();
recursive_reduce_string_tests();
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::cout << "Computation finished at " << std::ctime(&end_time) << "elapsed time: " << elapsed_seconds.count() << '\n';
return 0;
}
The output of the test code above:
recursive_reduce_string function test with std::vector<std::string>:
foobarbazquux
recursive_reduce_string function test with std::vector<std::vector<std::string>>:
foobarbazquuxfoobarbazquuxfoobarbazquux
recursive_reduce_string function test with std::array<std::string, 3>:
foobarbaz
recursive_reduce_string function test with std::array<std::array<std::string, 3>, 3>:
foobarbazfoobarbazfoobarbaz
recursive_reduce_string function test with execution policy, std::vector<std::string>:
foobarbazquux
recursive_reduce_string function test with execution policy, std::vector<std::vector<std::string>>:
foobarbazquuxfoobarbazquuxfoobarbazquux
recursive_reduce_string function test with initial value, std::vector<std::string>:
_foobarbazquux
recursive_reduce_string function test with initial value, std::vector<std::vector<std::string>>:
_foobarbazquuxfoobarbazquuxfoobarbazquux
recursive_reduce_string function test with execution policy, initial value, std::vector<std::string>:
_foobarbazquux
recursive_reduce_string function test with execution policy, initial value, std::vector<std::vector<std::string>>:
_foobarbazquuxfoobarbazquuxfoobarbazquux
recursive_reduce_string function test with initial value, specified operation, std::vector<std::string>:
_ foo bar baz quux
recursive_reduce_string function test with execution policy, initial value, specified operation, std::vector<std::string>:
_ foo bar baz quux
recursive_reduce_string function test with std::deque<std::string>:
1234
recursive_reduce_string function test with std::deque<std::deque<std::string>>:
123412341234
recursive_reduce_string function test with std::vector<std::wstring>:
0123
recursive_reduce_string function test with std::vector<std::vector<std::wstring>>:
0123012301230123
recursive_reduce_string function test with std::array<std::wstring, 4>:
1234
recursive_reduce_string function test with std::array<std::array<std::wstring, 4>, 4>:
1234123412341234
recursive_reduce_string function test with std::deque<std::wstring>:
0123
recursive_reduce_string function test with std::deque<std::deque<std::wstring>>:
0123012301230123
recursive_reduce_string function test with std::vector<std::u8string>:
€2.00€2.00€2.00€2.00
recursive_reduce_string function test with std::vector<std::vector<std::u8string>>:
€2.00€2.00€2.00€2.00€2.00€2.00€2.00€2.00€2.00€2.00€2.00€2.00
recursive_reduce_string function test with std::vector<std::pmr::string>:
123123123
Computation finished at Thu Oct 26 08:44:11 2023
elapsed time: 0.00210049
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A recursive_reduce Template Function with Unwrap Level Implementation in C++ and
A recursive_reduce_string Template Function Implementation in C++
What changes has been made in the code since last question?
Another approach of
recursive_reduce_string
template function implementation is proposed in this post.Why a new review is being asked for?
Please review the revised
recursive_reduce_string
template function implementation and all suggestions are welcome.