Search code examples
c++dictionarystliteratorencapsulation

Container class that encapsulates map with custom iterators


I'd like to make a class that encapsulates a std::map and acts like a container of a different type, like this:

class Item
{
public:
    Item(int number, const char *text) :
        Number(number),
        Text(text)
    {}

    int Number;
    std::string Text;
};

class Container
{
public:

    void add(Item item)
    {
        Map.insert(std::make_pair(item.Number, item.Text));
    }

    Container::iterator begin();
    Container::iterator end();

private:
    std::map <int, string> Map;
};

I want to use it like this, without using the map's first and second:

int main()
{
    Container container;
    container.add(Item(0, "foo"));
    container.add(Item(1, "bar"));

    for (auto &elem : container)
    {
        // elem is an Item
        cout << elem.Number << elem.Text << endl;
    }
}

I tried implementing a custom iterator that encapsulated a map iterator, but I got stuck on the -> operator, which must return the address of an Item object (right?) There's no Item object whose address I can return.

How do you do this?


Solution

  • I am not sure, but it looks like you are looking for structered bindings.

    Something like

    std::map<int, std::string> myMap{};
    
    for (const auto &[num, txt] : myMap) {
        // elem is an Item
        cout << num << ' ' << txt << '\n';
    }
    

    Please read here

    But if you are really interested in an implementation of an iterator for such a custom container, then please look below:

    #include <iostream>
    #include <string>
    #include <map>
    #include <iterator>
    #include <utility>
    #include <type_traits>
    
    using Pair = std::pair<int, std::string>;
    using Map = std::map<Pair::first_type, Pair::second_type>;
    
    struct Item {
        Pair::first_type number{};
        Pair::second_type text{};
    };
    
    class Container {
        Map data{};
    public:
    
        void add(Item item) { data.insert({ item.number, item.text }); }
    
        // Add iterator properties to class ---------------------------------------------------------------
        class iterator {                  // Local class for iterator
            Map::iterator iter{};         // This will be the internal iterator 
            Map::iterator begin{};        // For boundary checking
            Map::iterator end{};
    
            Item item;
        public:                                    // Define alias names necessary for the iterator functionality
            using iterator_category = std::random_access_iterator_tag;
            using difference_type = std::ptrdiff_t;
            using value_type = Item;
            using pointer = const Item* const;
            using reference = Item&;
    
            // Constructor
            iterator(const Map::iterator& i, const Map::iterator& ibegin, const Map::iterator& iend) : iter(i), begin(ibegin), end(iend) {};
    
            // Dereferencing
            value_type operator *() const { return { iter->first, iter->second }; }
            pointer operator ->() { item = { iter->first, iter->second }; return &item; }
    
            // Aithmetic operations
            iterator& operator ++() { if (iter != end) ++iter; return *this; }
            iterator& operator --() { if (iter != begin) --iter; return *this; }
            
            iterator operator ++(int) { iterator tmp{ *this }; ++iter; return tmp; }
            iterator operator --(int) { iterator tmp{ *this }; ++iter; return tmp; }
            iterator operator +(const difference_type& n) const { iterator tmp{ *this };  difference_type k{ n }; while (k--)++tmp; return tmp; }
            iterator& operator +=(const difference_type& n) { difference_type k{ n }; while (k--)++* this; return *this; };
            iterator operator -(const difference_type& n) const { iterator tmp{ *this };  difference_type k{ n }; while (k--)--tmp; return tmp; }
            iterator& operator -=(const difference_type& n) { difference_type k{ n }; while (k--)--* this; return *this; };
            
            // Comparison
            bool operator != (const iterator& other) const { return iter != other.iter; }
            bool operator == (const iterator& other) const { return iter == other.iter; }
            bool operator < (const iterator& other) const { return (other - *this) < 0; }
            bool operator > (const iterator& other) const { return (other - *this) > 0; }
            bool operator <= (const iterator& other) const { return (other - *this) <= 0; }
            bool operator >= (const iterator& other) const { return (other - *this) >= 0; }
    
            // Reference and difference. Also very questionable
            reference operator[] (const difference_type& n) {
                iterator tmp(begin, begin, end);  
                difference_type k{ n }; while (k--) ++tmp;
                item = *tmp;
                return item;
            }
            //Extermely complex and unefficient
            difference_type operator-(const iterator& other) const {
                difference_type result{};
                if (*this != other) {
                    iterator tmp(iter, begin, end);
    
                    while ((tmp.iter != other.iter) and tmp.iter != end) {
                        ++tmp;
                        ++result;
                    }
                    if (tmp.iter == end) {
                        result = 0;
                        iterator tmpOther(other.iter, other.begin, other.end);
                        while ((tmpOther.iter != iter) and tmpOther.iter != other.end) {
                            ++tmpOther;
                            --result;
                        }
                    }
                }
                return result;
            }
        };
        iterator begin() { return iterator(data.begin(), data.begin(), data.end()); }
        iterator end() { return iterator(data.end(), data.begin(), data.end()); }
    };
    // Test Code
    int main() {
        Container c;
        c.add({ 1,"111" });
        c.add({ 2,"222" });
        c.add({ 3,"333" });
        c.add({ 4,"444" });
    
        for (const Item& i : c)
            std::cout << i.number << ' ' << i.text << '\n';
    }