I'm trying to write an AsString() function that converts STL containers to string according to my taste. Here's the code I've come up with so far:
template<class T>
inline string AsString(const T& v);
template<class First, class Second>
inline string AsString(const pair<First, Second>& p);
template<class Iter>
inline string PrintSequence(const char* delimiters, Iter begin, Iter end) {
string result;
result += delimiters[0];
int size = 0;
for (size = 0; begin != end; ++size, ++begin) {
if (size > 0) {
result += ", ";
}
result += AsString(*begin);
}
result += delimiters[1];
result += StringPrintf("<%d>", size);
return result;
}
#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \
template<class T1, class T2> \
inline string AsString(const Sequence<T1, T2>& seq) { \
return PrintSequence("[]", seq.begin(), seq.end()); \
}
OUTPUT_TWO_ARG_CONTAINER(vector)
OUTPUT_TWO_ARG_CONTAINER(deque)
OUTPUT_TWO_ARG_CONTAINER(list)
template<class First, class Second>
inline string AsString(const pair<First, Second>& p) {
return "(" + AsString(p.first) + ", " + AsString(p.second) + ")";
}
template<class T>
inline string AsString(const T& v) {
ostringstream s;
s << v;
return s.str();
}
As you can see, the basic idea is that AsString() recursively calls itself on STL containers and then it bottoms out to the usual operator<<() (the reason I don't want to override operator<<() is because I don't want to interfere with other libraries that do exactly that).
Now, AsString() compiles and works on shallow containers, but not on nested ones:
vector<int> v;
v.push_back(1);
v.push_back(2);
AsString(v) == "[1, 2]<2>"; // true
vector<vector<int> > m;
m.push_back(v);
m.push_back(v);
AsString(m) == "[[1, 2]<2>, [1, 2]<2>]<2>"; // Compilation Error!!!
The compiler, for some reason, wants to use operator<<() when trying to print the elements of `m', despite the fact that I have provided a template specialization for vectors.
How could I make AsString() work?
UPDATE: OK, turns out the order of definitions do matter (at least for this compiler -- gcc 4.4.3). When I put the macro definitions first, the compiler will correctly pick them up and display a vector of vectors. Inexplicable.
The template world is wonderful... and a real trap to the unwary...
A specialization, is taking an existing template function and specifying all its arguments.
An overload is reusing the same name that another function (whether template or not) for a different set of arguments.
template <typename T>
void foo(T const& t);
template <>
void foo<int>(int i); // this is a "complete" specialization
template <typename T, typename U>
void foo<std::pair<T,U>>(std::pair<T,U> const& pair);
// this is a "partial" specialization
// and by the way... it does NOT COMPILE
template <typename T, typename U>
void foo(std::pair<T,U> const& pair); // this is an overload
Note the syntactical difference, in the overload there is no <xxxx> after the identifier (foo here).
It is not possible, in C++, to partially specialize a function; that is to leave some genericity in the arguments. You can either overload or fully specialize: mandatory reading at this point GotW #49: Template Specialization and Overloading
Therefore, the choice is between:
template <typename T>
std::string AsString(const T& v); // (1)
template <typename T, typename Allocator>
std::string AsString(std::vector<T, Allocator> const& v); // (2)
And the real question is: what is the type of *begin ?
Well, m is not const-qualified:
Iter logically is std::vector< std::vector<int> >::iterator.*begin is thus std::vector<int>&So the two overloads are considered with:
T = std::vector<int>, requires a conversion to const-refT = int, U = std::allocator<int>, requires a conversion to const-refThe second should be selected because it's closer to the real type, as far as I understand. I tested it with VC++ 2010 and it actually got selected.
Could you also declare a non-const qualified version of the vector overload and see if it appeases your compiler ? (which I'd like to know the name of, by the way ;) ).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With