Home > database >  Recursive tabulation while printing map of custom class
Recursive tabulation while printing map of custom class

Time:01-08

I have a class Twhich has a map of string class T

For convinience, I would like to print the content in the class in an organized manner with tabulations.

As an example, I have:

#include <iostream>
#include <map>
#include <string>

class test;

typedef std::map<std::string, std::string> sMap;
typedef std::map<std::string, test> testMap;

class test {
    public:
    sMap     map1;
    testMap  map2; 
};

std::ostream& operator<<(std::ostream& os, const sMap& smap) {
    for(const auto& s_smap : smap) {
        os  << s_smap.first 
            << "\t"
            << s_smap.second
            << "\n";
    }
    return os;
}

std::ostream& operator<<(std::ostream& os, const test& t) {
    os  << t.map1
        << "\n";

    for (const auto& s_map : t.map2) {
        os  << s_map.first 
            << "\t"
            << s_map.second
            << std::endl;
    }
    return os;
}

int main() {
    sMap myMap; 
    myMap["a"] = "b";

    test obj;
    obj.map1 = myMap;

    test obj2;
    obj2.map2.insert({"one", obj});
    obj2.map2["one"].map2.insert({"two", obj});
    obj2.map2["one"].map2["two"].map2.insert({"three", obj});
    obj2.map2["one"].map2["two"].map2["three"].map2.insert({"four", obj});

    std::cout << obj2 << std::endl;

    return 0;
}

I would like the output to be:

one     a       b

    two     a       b

        three   a       b

            four    a       b

How can the operator<< be overloaded to achieve this?

CodePudding user response:

Doing this means you need to pass along extra information. You could create a printMap kind of fucntion that takes an indentation level as an argument, but you've said you want to achieve this by overloading operator<< so you can't pass that along unless you wrap your test objects in another class/struct that does carry that info.

That might look something like the below, which outputs:

% ./a.out
    a   b
        a   b
            a   b
                a   b


Tweaking this is left as an exercise for the OP.

#include <iostream>
#include <map>
#include <string>

struct test;

using sMap = std::map<std::string, std::string>;
using testMap = std::map<std::string, test>;

struct test {
    sMap     map1;
    testMap  map2; 
};

struct indent {
    const test &ref;
    int indentLevel;    

    indent(const test &ref, int indentLevel=0) 
    : ref(ref), indentLevel(indentLevel) 
    { } 
};

std::ostream& operator<<(std::ostream& os, const indent &i) {
    int j = i.indentLevel;

    for (const auto &[f, s] : i.ref.map1) {
        for (int k = 0; k < j; k  ) os << "\t";
        os << f << "\t" << s << "\n";        
    }

    for (const auto &[f, s] : i.ref.map2) {
        os << indent(s, j   1);
    }

    return os;
}

std::ostream& operator<<(std::ostream& os, const sMap& smap) {
    for (const auto &[f, s] : smap) {
        os  << f << "\t" << s << "\n";
    }
    return os;
}

std::ostream& operator<<(std::ostream& os, const test& t) {
    os  << t.map1
        << "\n";

    for (const auto& s_map : t.map2) {
        os  << s_map.first 
            << "\t"
            << s_map.second
            << std::endl;
    }
    return os;
}

int main() {
    sMap myMap; 
    myMap["a"] = "b";

    test obj;
    obj.map1 = myMap;

    test obj2;
    obj2.map2.insert({"one", obj});
    obj2.map2["one"].map2.insert({"two", obj});
    obj2.map2["one"].map2["two"].map2.insert({"three", obj});
    obj2.map2["one"].map2["two"].map2["three"].map2.insert({"four", obj});

    std::cout << indent(obj2) << std::endl;

    return 0;
}

Some aspects of this case incorporate post-C 11 features like structured bindings. If a newer standard cannot be used, these techniques can be converted to C 11 friendly constructs.

  • Related