This is a follow-up question for A recursive_transform Template Function Implementation with std::invocable concept in C++, A recursive_transform Template Function Implementation with std::invocable Concept and Execution Policy in C++, A recursive_transform for std::vector with various return type and A recursive_invoke_result_t template structure. Thanks to indi's answer. After checked the mentioned "completely different way", I am trying to implement another version recursive_transform
function here. A recursive version std::invoke_result_t
is used instead of std::indirect_result_t
to determine the type of output structure. Therefore, the structure
using TransformedValueType = decltype(recursive_transform(*input.begin(), f));
Container<TransformedValueType> output;
turns into recursive_invoke_result_t<F, Range> output{};
.
The implementation of recursive_invoke_result_t
template structure is from HTNW's answer.
The experimental implementation
The experimental implementation is as below.
// recursive_invoke_result_t implementation
// from https://stackoverflow.com/a/65504127/6667035
template<typename, typename>
struct recursive_invoke_result { };
template<typename T, std::invocable<T> F>
struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; };
template<typename F, template<typename...> typename Container, typename... Ts>
requires (
!std::invocable<F, Container<Ts...>> &&
std::ranges::input_range<Container<Ts...>> &&
requires { typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type; })
struct recursive_invoke_result<F, Container<Ts...>>
{
using type = Container<typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type>;
};
template<typename F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result<F, T>::type;
template <std::ranges::range Range>
constexpr auto get_output_iterator(Range& output)
{
return std::inserter(output, std::ranges::end(output));
}
template <class T, std::invocable<T> F>
constexpr auto recursive_transform(const T& input, const F& f)
{
return f(input);
}
template <
std::ranges::input_range Range,
class F>
requires (!std::invocable<F, Range>)
constexpr auto recursive_transform(const Range& input, const F& f)
{
recursive_invoke_result_t<F, Range> output{};
auto out = get_output_iterator(output);
std::ranges::transform(
input.cbegin(),
input.cend(),
out,
[&f](auto&& element) { return recursive_transform(element, f); }
);
return output;
}
Test cases
Considering G. Sliepen's answer, the std::vector<std::string>
test cases is added.
// non-nested input test, lambda function applied on input directly
int test_number = 3;
std::cout << recursive_transform(test_number, [](int element) { return element + 1; }) << std::endl;
// nested input test, lambda function applied on input directly
std::vector<int> test_vector = {
1, 2, 3
};
std::cout << recursive_transform(test_vector, [](std::vector<int> element)
{
auto output = element;
output.push_back(4);
return output;
}).size() << std::endl;
// std::vector<int> -> std::vector<std::string>
auto recursive_transform_result = recursive_transform(
test_vector,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "std::vector<int> -> std::vector<std::string>: " +
recursive_transform_result.at(0) << std::endl; // recursive_transform_result.at(0) is a std::string
// std::vector<string> -> std::vector<int>
std::cout << "std::vector<string> -> std::vector<int>: "
<< recursive_transform(
recursive_transform_result,
[](std::string x) { return std::atoi(x.c_str()); }).at(0) + 1 << std::endl; // std::string element to int
// std::vector<std::vector<int>> -> std::vector<std::vector<std::string>>
std::vector<decltype(test_vector)> test_vector2 = {
test_vector, test_vector, test_vector
};
auto recursive_transform_result2 = recursive_transform(
test_vector2,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "string: " + recursive_transform_result2.at(0).at(0) << std::endl; // recursive_transform_result.at(0).at(0) is also a std::string
// std::deque<int> -> std::deque<std::string>
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(1);
test_deque.push_back(1);
auto recursive_transform_result3 = recursive_transform(
test_deque,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "string: " + recursive_transform_result3.at(0) << std::endl;
// std::deque<std::deque<int>> -> std::deque<std::deque<std::string>>
std::deque<decltype(test_deque)> test_deque2;
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);
auto recursive_transform_result4 = recursive_transform(
test_deque2,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "string: " + recursive_transform_result4.at(0).at(0) << std::endl;
// std::list<int> -> std::list<std::string>
std::list<int> test_list = { 1, 2, 3, 4 };
auto recursive_transform_result7 = recursive_transform(
test_list,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "string: " + recursive_transform_result7.front() << std::endl;
// std::list<std::list<int>> -> std::list<std::list<std::string>>
std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
auto recursive_transform_result8 = recursive_transform(
test_list2,
[](int x)->std::string { return std::to_string(x); }); // For testing
std::cout << "string: " + recursive_transform_result8.front().front() << std::endl;
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 for std::vector with various return type and
What changes has been made in the code since last question?
I am trying to implement another version
recursive_transform
function withrecursive_invoke_result_t
andstd::ranges::transform
here.Why a new review is being asked for?
A recursive version
std::invoke_result_t
is used instead of something likesomething_something_recursively_indirect_result_t
because I am not sure how to usestd::indirect_result_t
correctly. If the idea of usingstd::indirect_result_t
is still better, please let me know. On the other hand, if there is any good example ofstd::indirect_result_t
usage available, please also tell me.