Reputation: 7788
Firstly, code for reference (don't worry about <<
overloads, originally everything is in namespace):
#include <bits/stdc++.h>
// structures for checking if given variable is container
template <typename Container>
struct is_container : std::false_type { };
template<typename... Ts>
struct is_container<std::vector<Ts...>> : std::true_type { };
template<typename... Ts>
std::ostream& operator << (std::ostream& os, std::vector<Ts...>) {return os;}
template<typename... Ts>
struct is_container<std::deque<Ts...>> : std::true_type { };
template<typename... Ts>
std::ostream& operator << (std::ostream& os, std::deque<Ts...>) {return os;}
template<typename... Ts>
struct is_container<std::list<Ts...>> : std::true_type { };
template<typename... Ts>
std::ostream& operator << (std::ostream& os, std::list<Ts...>) {return os;}
//... the rest of containers in like manner, not necessary for this example
//function that prints given container (begin() and end() are required)
//this function is instantiated also for non-printable containers, but then is never called
//that's why I had to overload << operator, so it may instantiate for them
template<template<typename ...>
class C ,
typename T>
void print(const C<T>& cont,
const char& separator = ',',
const std::deque<std::pair<char,char>>& closures = {{'(', ')'}});
//function that prints to console given nested container. Container has to be at least one-level nested
//so it will accept f.e. vector of vectors of ints, but will reject vector of int
template<int Depth,
template<typename ...>
class C ,
typename T,
typename = typename std::enable_if<is_container<T>::value>::type>
void print_nested(const C<T>& cont,
const char& separator = ',',
const std::deque<std::pair<char,char>>& closures = {{'{','}'},{'[',']'},{'(',')'}},
unsigned depth = 0 );
//placebo template function to handle calls, where containers aren't nested (kind of fake instantiation)
template<int Depth, typename T>
void print_nested(const T&,
const char& ,
const std::deque<std::pair<char,char>>&,
unsigned);
//definition of above
template<int Depth, typename T>
void print_nested(const T&,
const char& ,
const std::deque<std::pair<char,char>>&,
unsigned)
{}
//definition of function printing non-nested container
template<template<typename ...> class C , typename T>
void print(const C<T>& cont,
const char& separator,
const std::deque<std::pair<char,char>>& closures)
{
std::cout << closures[0].first;
for(auto it=cont.begin(); it!=cont.end(); )
{
std::cout << *it;
if(++it != cont.end())
std::cout << separator << " ";
}
std::cout << closures[0].second;
}
//definition of main function, which handles printing nested containers
template<int Depth,
template<typename ...> class C ,
typename T,
typename = typename std::enable_if<is_container<T>::value>::type>
void print_nested(const C<T>& cont,
const char& separator,
const std::deque<std::pair<char,char>>& closures,
unsigned depth)
{
if(depth < Depth)
++depth;
std::cout << closures[0].first;
for(auto it=cont.begin(); it!=cont.end(); )
{
if(it!=cont.begin())
std::cout << std::string(depth, ' ');
if(is_container<typename T::value_type>::value)
print_nested<Depth>(*it,
separator,
(closures.size() > 1)?
std::deque<std::pair<char,char>>(closures.begin()+1, closures.end()) : closures,
depth
);
else
print(*it,
separator,
(closures.size() > 1)?
std::deque<std::pair<char,char>>(closures.begin()+1, closures.end()) : closures
);
if(++it != cont.end())
std::cout << separator << std::endl;
}
std::cout << closures[0].second;
}
void gimme_some_space(std::string anger = "")
{
std::for_each(anger.begin(), anger.end(), [](char c) {if(c=='!') std::cout << std::endl;});
}
int main()
{
//cases and calls:
std::vector<std::deque<std::list<int>>> nested {{{1,2,3},{3,4,5},{7,8,9}},{{9,8,7},{6,5,4},{3,2,1}}};
std::vector<int> not_nested {1,2,3,4,5};
print_nested<2>(nested); // target functionality
gimme_some_space("!!");
print_nested<0>(nested); // i'd like to omit <0> (set it as default non-type template parameter)
//print_nested<0>(not_nested); //excluded from overload set, non_nested is not nested
gimme_some_space("!!!");
print(not_nested); //call for printing non-nested container
gimme_some_space("!!!!");
}
Does it's job. Here is output:
{[(1, 2, 3),
(3, 4, 5),
(7, 8, 9)],
[(9, 8, 7),
(6, 5, 4),
(3, 2, 1)]}
{[(1, 2, 3),
(3, 4, 5),
(7, 8, 9)],
[(9, 8, 7),
(6, 5, 4),
(3, 2, 1)]}
(1, 2, 3, 4, 5)
My question is: The parameter Depth decides about indents. It always should be equal to level of nesting, for non-nested container it should be 0. But I don't even expect it to somehow retrieve information about how much nested is given container - on the contrary - I'd like to set Depth default value to zero. So in template parameters it should be:
int Depth = 0
But that complicates the matter. If it has default value, it cannot be placed at the beggining of the parameter list, because Container C, and element of that container T (types of course) aren't default. So let's do that anyway:
template<template<typename ...> class C ,
typename T,
int Depth = 0,
typename = typename std::enable_if<is_container<T>::value>::type>
Almost perfect. Is it? This now works with Depth set default to 0:
print_nested(nested);
But what if I now want to specify indents myself. Horror:
print_nested<std::vector<std::deque<std::list<int>>>, std::deque<std::list<int>>, 2>(nested);
Do you know any mind-blowing method to overcome this?
Upvotes: 1
Views: 919
Reputation: 217085
You may use extra argument.
How about std::integral_constant<std::size_t, Depth > maxDepth
, so
template <std::size_t N> using Depth_t = std::integral_constant<std::size_t, N>;
template<template<typename ...> class C ,
typename T,
std::size_t Depth = 0,
typename = typename std::enable_if<is_container<T>::value>::type>
void print_nested(const C<T>& cont,
Depth_t<Depth> = {},
const char& separator = ',',
const std::deque<std::pair<char,char>>& closures = {{'{','}'},{'[',']'},
unsigned depth = 0
);
And call it so
print_nested(nested, Depth_t<2>{});
Upvotes: 2