Matthew Hoggan
Matthew Hoggan

Reputation: 7604

getting an iterator to a generic container inside a generic class: HOW TO

This comes from a larger context, however I have stripped alot of code to simplify the question. If you feel I have left anything out please let me know.

Lets say you have a template class defined as:

#include <map>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <iterator>
#include <algorithm>
template <class key, class container, class container_type>
class file_to_map
{
public:
  file_to_map()
  {
    m_file = "";
  }

  file_to_map(std::string file)
  {
    m_file = file;
  }

  ~file_to_map()
  {
  }

  std::map<key, container>& get_map()
  {
    return m_map;
  }

  void set_file(const std::string file)
  { 
    m_file = file;
  }

  void insert_into_map(key insert, container_type value)
  {
    m_map[insert].insert(value);
  }

  friend std::ostream& operator<< (std::ostream &out, file_to_map<key, container, container_type> &obj)
  {
    typedef typename std::map<key, container>::const_iterator mapItr;
    mapItr mbi = obj.m_map.begin();
    mapItr emi = obj.m_map.end();
    while (mbi != emi) {
      out << " -- " << mbi->first << " -- " << std::endl;
      container::iterator cbi;
      ++mbi;
    }
    return out;
  }

  friend std::istream& operator>> (std::ifstream &in, file_to_map<key, container, container_type> &obj)
  {
    if (in.is_open())
      in.close();

    if (obj.m_file == "")
      return in;

    in.open(obj.m_file.c_str(), std::ios::in);

    if (in.fail() || in.bad()) {
      in.close();
      return in;
    }

    std::vector<key> tmp;
    typedef std::istream_iterator<key> string_input;
    copy(string_input(in), string_input(), back_inserter(tmp));
    typename std::vector<key>::iterator bvi = tmp.begin();
    typename std::vector<key>::iterator evi = tmp.end();
    while (bvi != evi) {
      obj.m_map[*(bvi)] = container();
      ++bvi;
    }

    in.close();
    return in;
  }  
  private:
    std::map<key, container> m_map;
    std::string m_file;
};

and inside the friend method "operator<<"

You want to print the conents of the map and the generic container how would one go about doing this? How do you get an appropriate iterator to iterate through the contents of the generic container.

I am trying it with:

container::iterator cbi;

CODE UPDATE

With:

friend std::ostream& operator<< (std::ostream &out, file_to_map<key, container, container_type> &obj)
  {
    typedef typename std::map<key, container>::const_iterator mapItr;
    mapItr mbi = obj.m_map.begin();
    mapItr emi = obj.m_map.end();
    while (mbi != emi) {
      out << " -- " << mbi->first << " -- " << std::endl;
      typename container::const_iterator cbi = mbi->second.begin();
      typename container::const_iterator ebi = mbi->second.end();
      std::copy(cbi, mbi, std::ostream_iterator<container_type>(out, "\t\n"));
      ++mbi;
    }
    return out;
  }

I am getting the following compiler error:

g++ -o file_to_map -Wall ./file_to_map.h ./main.cpp ./code_finder.cpp \
        -L/usr/local/boost_1_48_0/stage/lib -lboost_filesystem -lboost_system -I /usr/local/boost_1_48_0/
In file included from ./code_finder.h:4,
                 from ./code_finder.cpp:1:
./file_to_map.h: In function ‘std::ostream& operator<<(std::ostream&, file_to_map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)’:
./code_finder.cpp:36:   instantiated from here
./file_to_map.h:62: error: no matching function for call to ‘copy(std::_Rb_tree_const_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >&, operator<<(std::ostream&, file_to_map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)::mapItr&, std::ostream_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >)’

What am I missing?

Upvotes: 0

Views: 1091

Answers (1)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361782

Use typename:

typename container::iterator cbi;

Because iterator is a dependent name, as it depends on the type argument container of the class template. Interestingly, if you've corrected used typename in other place, such as in operator<<:

typedef typename std::map<key, container>::const_iterator mapItr;

Since you're working with const_iterator for the map which means the container objects which you would get using map's const iterator will be const objects, which in turn implies you need to use const_iterator for the containers as well, because they are the values of the map. So I think you need to use this:

typename container::const_iterator cbi; //use this instead!

Reply to your edit:

I see a typo here:

typename container::const_iterator cbi = mbi->second.begin();
typename container::const_iterator ebi = mbi->second.end();
std::copy(cbi, mbi, std::ostream_iterator<container_type>(out, "\t\n"));
            // ^^^ typo

The second argument to std::copy should be ebi, not mbi. That is why I usually name such variables as begin and end, instead of cbi and ebi.

Upvotes: 7

Related Questions