Search code examples
c++templatesrecursionpretty-print

Generic print function which behaves handles both lists as well as scalar entities?


I'm trying to write a function which I can call whenever I need to print something. However, when printing a vector or list I need it to behave slightly differently. Also, a vector or list can contain another vector or list.

My first approach was to do something like this:

#include <iostream>
#include <list>
#include <vector>

using namespace std;

template <typename T>
void
print(const vector<T>& v) {
    cout << "[";
    bool isFirst = true;
    for (const T& e : v) {
        if (isFirst) isFirst = false;
        else cout << ",";
        print(e);
    }
    cout << "]";
}

template <typename T>
void
print(const list<T>& l) {
    cout << "[";
    bool isFirst = true;
    for (const T& e : l) {
        if (isFirst) isFirst = false;
        else cout << ",";
        print(e);
    }
    cout << "]";
}

template <typename T>
void
print(const T& v) {
    cout << v;
}

int
main(int argc, char** argv) {
    vector<int> v;
    print(v);
    return 0;
}

As you see, there is a lot of code duplication for printing vectors and lists, but I don't know how to combine these. In any case, the code doesn't compile as the compiler tries to match printing of a scalar entity (such as int) with the first implementation of print instead of the last implementation:

g++ -std=c++11 test.cpp
test.cpp: In instantiation of ‘void print(const std::vector<T>&) [with T = int]’:
test.cpp:42:12:   required from here
test.cpp:15:16: error: no matching function for call to ‘print(const int&)’
         print(e);
                ^
test.cpp:15:16: note: candidate is:
test.cpp:9:1: note: template<class T> void print(const std::vector<T>&)
 print(const vector<T>& v) {
 ^
test.cpp:9:1: note:   template argument deduction/substitution failed:
test.cpp:15:16: note:   mismatched types ‘const std::vector<T>’ and ‘const int’
         print(e);
                ^

Any idea of how I can solve this?


Solution

  • There are many neat solutions, but one that is quite close to yours without duplication, without anything too smart and without any library function is the following

    template <typename T>
    void print_container(const T&);
    
    template <typename T>
    void print(const std::vector<T>& v) { print_container(v); }
    
    template <typename T>
    void print(const std::list<T>& l) { print_container(l); }
    
    template <typename T>
    void print(const T& e) { cout << e; }
    
    template <typename T>
    void print_container(const T& c) {
        cout << "[";
        bool isFirst = true;
        for (const auto& e : c) {
            if (isFirst) isFirst = false;
            else cout << ",";
            print(e);
        }
        cout << "]";
    }