This is a follow-up question for A recursive_depth
function for calculating depth of nested types implementation in C++. I am rethinking about the proposed recursive_depth
function. When it comes to the type std::string
, the output is always 1
because the base type is char
. However, why it is 1
, not 0
from the perspective of encapsulation? Therefore, I am trying to implement another version recursive_depth
function with target type in this post. Additional input here is the target base type, the output 0
will be given for the type std::string
when target base type is equal to std::string
.
The experimental implementation
recursive_depth
template function implementation// recursive_depth function implementation with target type template<typename T_Base, typename T> constexpr std::size_t recursive_depth() { return std::size_t{0}; } template<typename T_Base, std::ranges::input_range Range> requires (!std::same_as<Range, T_Base>) constexpr std::size_t recursive_depth() { return recursive_depth<T_Base, std::ranges::range_value_t<Range>>() + std::size_t{1}; }
Full Testing Code
The full testing code:
// A recursive_depth Function Implementation with Target Type Parameter in C++
#include <algorithm>
#include <array>
#include <concepts>
#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <ranges>
// recursive_depth function implementation with target type
template<typename T_Base, typename T>
constexpr std::size_t recursive_depth()
{
return std::size_t{0};
}
template<typename T_Base, std::ranges::input_range Range>
requires (!std::same_as<Range, T_Base>)
constexpr std::size_t recursive_depth()
{
return recursive_depth<T_Base, std::ranges::range_value_t<Range>>() + std::size_t{1};
}
void recursive_depth_test();
int main()
{
recursive_depth_test();
}
void recursive_depth_test()
{
// std::string cases
std::string test_string = "123";
std::cout << "Giving base type std::string:" << recursive_depth<decltype(test_string), decltype(test_string)>() << '\n';
std::cout << "Giving base type char:" << recursive_depth<char, decltype(test_string)>() << '\n';
// non-nested type `char` case
char test_char = 'A';
std::cout << "non-nested type `char` case: " << recursive_depth<char, decltype(test_char)>() << '\n';
// non-nested type `int` case
int test_int = 100;
std::cout << "non-nested type `int` case: " << recursive_depth<int, decltype(test_int)>() << '\n';
// std::vector<int> case
std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
std::cout << "std::vector<int> case: " << recursive_depth<int, decltype(test_vector)>() << '\n';
// std::vector<std::vector<int>> case
std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
std::cout << "std::vector<std::vector<int>> case: " << recursive_depth<int, decltype(test_vector2)>() << '\n';
// std::deque<int> case
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(2);
test_deque.push_back(3);
test_deque.push_back(4);
test_deque.push_back(5);
test_deque.push_back(6);
std::cout << "std::deque<int> case: " << recursive_depth<int, decltype(test_deque)>() << '\n';
// std::deque<std::deque<int>> case
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);
std::cout << "std::deque<std::deque<int>> case: " << recursive_depth<int, decltype(test_deque2)>() << '\n';
// std::list<int> case
std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
std::cout << "std::list<int> case: " << recursive_depth<int, decltype(test_list)>() << '\n';
// std::list<std::list<int>> case
std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
std::cout << "std::list<std::list<int>> case: " << recursive_depth<int, decltype(test_list2)>() << '\n';
std::cout << "std::list<std::list<int>> with given base type std::list<int>: " << recursive_depth<std::list<int>, decltype(test_list2)>() << '\n';
}
The output of the test code above:
Giving base type std::string:0
Giving base type char:1
non-nested type `char` case: 0
non-nested type `int` case: 0
std::vector<int> case: 1
std::vector<std::vector<int>> case: 2
std::deque<int> case: 1
std::deque<std::deque<int>> case: 2
std::list<int> case: 1
std::list<std::list<int>> case: 2
std::list<std::list<int>> with given base type std::list<int>: 1
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A
recursive_depth
function for calculating depth of nested types implementation in C++.What changes has been made in the code since last question?
I am trying to implement another version
recursive_depth
function with target type in this post.Why a new review is being asked for?
Should it keeps the name
recursive_depth
or it's better to rename it by something likeunwrap_times_to_target_type
? Also, other suggestions are welcome.
-
\$\begingroup\$ Why do you construct objects in your test cases if you are only testing static properties of the types? \$\endgroup\$chrysante– chrysante2023年07月25日 10:03:34 +00:00Commented Jul 25, 2023 at 10:03
-
1\$\begingroup\$ get_nested_type_depth() , get_depth() . Possibly type_depth() \$\endgroup\$Lozminda– Lozminda2023年07月27日 01:46:07 +00:00Commented Jul 27, 2023 at 1:46
1 Answer 1
What happens in the case the value_type
being searched for isn't in the nested set of value_type
s? Consider recursive_depth<int, std::string>
. The value returned will be 2 ([std::basic_string<char>, char]
) because int
is never encountered. You could also scan the elements of tuple
, pair
, and ranges::subrange
elements. The other 2 tuple
-like types are std::array
(already handled) and std::complex
(undefined behavior if not arithmetic types, unspecified behavior if not standard floating point).
Your tests have run-time operations when you are working on compile-time types. Creating variables and filling them with dummy values doesn't seem within scope.
While it's nice you created readable tests, the cognitive load on the tester to evaluate every test as they read each one is quite burdensome. Calculate the result manually, verify it is correct, program your test with that expected value checked against the observed value. Testing frameworks usually have some sort of static test function that allows you to switch between compile time
reporting (hard error) and runtime reporting (onscreen fail message) at the flip of a flag (e.g. Catch2 v3 has STATIC_CHECK
).
Explore related questions
See similar questions with these tags.