0

I am tring to write a function to print generic map. this is what I have so far:

    template<typename map_key, typename map_val>
    void log(DEBUG_LEVEL level, std::map<map_key, map_val> _map) {
        if (level >= d_level) {
            for (std::map<map_key, map_val>::iterator it = _map.begin();
                    it != _map.end(); ++it)
                std::cout << it->first << " => " << it->second << '\n';
        }
    }

it doesn't compile.

error: expected ';' before 'it'
error: 'it' was not declared in this scope
  1. why does this isn't compiling ?
  2. is there a better way to write this ? I found a one liner to print a vector here (the second answer)and I wondered if there is the same for a map ?

update: I fix the (1) but still waiting for an answer to (2) I liked Galik answer but when I tried to add it as a function I got an error: error:'std::ostream& Logger::operator<<(std::ostream&, const std::pair<const _Key, _Tp>&)' must take exactly one argument

here is my Logger class:

extern DEBUG_LEVEL d_level;

class Logger {

public:

    Logger(const char * app_name);
    Logger(DEBUG_LEVEL level, char * app_name);
    void log(DEBUG_LEVEL level, const char* str, ...);

    template<typename vector_type>
    void log(DEBUG_LEVEL level, const std::vector<vector_type>& _vec,
            const std::string seperator = ", ") {
        if (level >= d_level) {
            std::cout << get_prefix_msg() << " ";
            change_color(level);
            std::cout << "[";
            std::copy(_vec.begin(), _vec.end(),
                    std::ostream_iterator<vector_type>(std::cout,
                            seperator.c_str()));
            std::cout << "]\n";
            printf(ANSI_COLOR_RESET);
        }
    }

    template<typename map_key, typename map_val>
    void log(DEBUG_LEVEL level, const std::map<map_key, map_val>& _map,
            const std::string seperator = ", ") {
        if (level >= d_level) {
            std::cout << get_prefix_msg() << " \n";
            change_color(level);
            std::cout << "[";
            for (typename std::map<map_key, map_val>::const_iterator it =
                    _map.begin(); it != _map.end(); ++it)
                std::cout << it->first << " => " << it->second << seperator;
            std::cout << "]\n";
            printf(ANSI_COLOR_RESET);
        }
    }

    /**
     * will log but without the application _app_name and without the debug level (but will have the right color)
     */
    void strip_log(DEBUG_LEVEL level, const char* str, ...);

    void change_verbosity(DEBUG_LEVEL level);

    static int msg_id;
private:
    const char * _app_name;
    void change_color(DEBUG_LEVEL level, bool is_strip = false);
    int get_next_msg_id();
    std::string get_prefix_msg();
    static DEBUG_LEVEL d_level;

};
Community
  • 1
  • 1
yehudahs
  • 2,488
  • 8
  • 34
  • 54
  • 3
    Might want to pass your map by const reference like `void log(DEBUG_LEVEL level, const std::map& _map)` since you're not changing it and it may be large. – Component 10 Apr 30 '15 at 07:46
  • 3
    Missing `typename`, and in C++11, just do `for (const auto& p : _map) {std::cout << p.first << " => " << p.second << '\n';}` – Jarod42 Apr 30 '15 at 07:48

2 Answers2

4

typename is missing. It should be

template<typename map_key, typename map_val>
void log(DEBUG_LEVEL level, const std::map<map_key, map_val>& _map) {
    if (level >= d_level) {
        for (typename std::map<map_key, map_val>::const_iterator it = _map.begin(); it != _map.end(); ++it)
            std::cout << it->first << " => " << it->second << '\n';
    }
}

or in C++11

template<typename map_key, typename map_val>
void log(DEBUG_LEVEL level, const std::map<map_key, map_val>& _map) {
    if (level >= d_level) {
        for (const auto& p : _map)
            std::cout << p.first << " => " << p.second << '\n';
    }
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    or even `template void log(DEBUG_LEVEL level, const MAP & map) { .... }` – Michael Anderson Apr 30 '15 at 07:51
  • 1
    @MichaelAnderson: It is not equivalent (OP may want to also write overload for `std::vector` for example) but it is simpler and may be enough. (and the error message would be more confusing when calling it with bad type). – Jarod42 Apr 30 '15 at 07:56
  • Yep, it certainly has its pros and cons. For example it will work if you switch to a `hash_map` or `unordered_map` or whatever. But will complain in an unhelpful way if you pass it a `vector`... – Michael Anderson Apr 30 '15 at 08:33
3

In answer to your second question, you might consider being even more generic and allowing more different types of containers.

Something like:

#include <map>
#include <set>
#include <vector>
#include <iostream>

typedef int DEBUG_LEVEL;
int d_level = 0;

template<typename Key, typename Value>
std::ostream& operator<<(std::ostream& os, const std::pair<const Key, Value>& p)
{
    os << p.first << " => " << p.second;
    return os;
}

template<typename Container>
void log(DEBUG_LEVEL level, const Container& c) {
    if (level >= d_level) {
        for(typename Container::const_iterator it = c.begin();
            it != c.end(); ++it)
        std::cout << *it << '\n';
    }
}

// OPTIONAL Adding your own types

class MyClass
{
    int i;
    std::string s;
public:
    MyClass(int i, std::string s): i(i), s(s) {}

    // declare the operator<<() overload as a friend to grant access
    // to private members
    friend std::ostream& operator<<(std::ostream& os, const MyClass& mc);

};

// define the operator<<() for MyClass as a global function (required)
std::ostream& operator<<(std::ostream& os, const MyClass& mc)
{
    os << "{MyClass: " << mc.s << ": " << mc.i << "}";
    return os;
}


// End of OPTIONAL

int main()
{
    std::set<int> s;
    s.insert(6);
    s.insert(3);
    s.insert(4);

    std::map<int, int> m;// {{1, 2}, {3, 4}};
    m[1] = 2;
    m[3] = 4;

    std::vector<int> v;
    v.push_back(4);
    v.push_back(3);
    v.push_back(2);
    v.push_back(1);

    std::cout << "\nset:\n";
    log(1, s);

    std::cout << "\nmap:\n";
    log(1, m);

    std::cout << "\nvector:\n";
    log(1, v);

    std::cout << "\nvector of MyClass:\n";
    std::vector<MyClass> vmc;
    vmc.push_back(MyClass(1, "hello"));
    vmc.push_back(MyClass(2, "world"));

    log(1, vmc);
}

Output:

set:
3
4
6

map:
1 => 2
3 => 4

vector:
4
3
2
1

vector of MyClass:
{MyClass: hello: 1}
{MyClass: world: 2}
Galik
  • 47,303
  • 4
  • 80
  • 117
  • I like this answer BUT - how do you add those functions to be inside a class ? I have a "Logger" class and I want those methode to be in it. When I tried to copy past it into my class I got the next error : error:'std::ostream& Logger::operator<<(std::ostream&, const std::pair&)' must take exactly one argument – yehudahs Apr 30 '15 at 15:31
  • @yehudahs You can't put the `operator<<()` inside a class, it has to be a global function. Its the way we extend the functionality of `std::cout << i;` style statements. You can also add your own types by defining an overload like the one in my example does for a `std::pair`. – Galik Apr 30 '15 at 15:58
  • @yehudahs I added an OPTIONAL section to the example to show how you can add your own classes to the output. – Galik Apr 30 '15 at 16:07