proton
proton

Reputation: 47

comparator for nested map

I have a map which contains another map as value.

Outer map contains string names/custom class (in this example i took name as example), inside map contain datetime and value.

I want CompareNames to be running for outer map and CompareDateTime to be running for inside map. Can i please get some help with what am i doing wrong while passing the comparators to the MyMap initializer list in struct A.

#include <iostream>
#include <map>
#include <locale>
#include <string>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>

enum class ComparePoilicy
{
    custom1,
    custom2
};

struct CompareNames
{
    explicit CompareNames(ComparePoilicy policy)
        : policy(policy)
    {}

    template <typename T>
    bool operator()(const T& lhs, const T& rhs) const
    {
        if (policy == ComparePoilicy::custom1)
        {
            return lhs < rhs;
        }
        else
        {
            return lhs > rhs;
        }
    }

    ComparePoilicy policy;
};

struct CompareDateTime
{
    explicit CompareDateTime(ComparePoilicy policy)
        : policy(policy)
    {}

    template <typename T>
    bool operator()(const T& lhs, const T& rhs) const
    {
        const boost::posix_time::ptime timelhs =
            boost::posix_time::time_from_string(lhs);
        const boost::posix_time::ptime timerhs =
            boost::posix_time::time_from_string(rhs);
        if (policy == ComparePoilicy::custom1)
        {
            return timelhs < timerhs;
        }
        else
        {
            return timelhs > timerhs;
        }
    }

    ComparePoilicy policy;
};

struct A
{
    explicit A(ComparePoilicy dateTime, ComparePoilicy names)
        : MyMap( CompareNames(names), CompareDateTime(dateTime))
    {}
    
    void fillMe()
    {
        MyMap["alpha"]["1981-08-20 08:05:00"] = 1;
        MyMap["alpha"]["1981-08-20 10:05:00"] = 1;
        MyMap["alpha"]["1981-08-20 09:05:00"] = 1;
        MyMap["gamma"]["1981-08-20 08:05:00"] = 1;
        MyMap["gamma"]["1981-08-20 10:05:00"] = 1;
        MyMap["gamma"]["1981-08-20 09:05:00"] = 1;    
        MyMap["beta"]["1981-08-20 08:05:00"] = 1;
        MyMap["beta"]["1981-08-20 10:05:00"] = 1;
        MyMap["beta"]["1981-08-20 09:05:00"] = 1;
    }
    
    void printMe()
    {
        for (auto& item : MyMap)
        {
            for (auto& entry : item.second)
            {
                std::cout << item.first << "  :  " << entry.first << " :  " << entry.second << std::endl;
            }
        }
    }
    
    std::map<std::string, std::map<std::string, int, CompareDateTime>, CompareNames> MyMap;
};


int main()
{
    A test(ComparePoilicy::custom1, ComparePoilicy::custom2);
    test.fillMe();
    test.printMe();

    return 0;
}

colliru link: http://coliru.stacked-crooked.com/a/2bdfbf3bd96ed17e

i tried searching for simillar issues, reading https://en.cppreference.com/w/cpp/container/map/map and trying to get the solution and playing with the initializer list in struct A.

Upvotes: 1

Views: 150

Answers (1)

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

In short, to use std::map::operator[], the mapped type must be default constructible and your std::map uses a comparator that is not.

You can work around it by a avoiding member functions that require the mapped type to be default constructible:

struct A {
    explicit A(ComparePoilicy dateTime, ComparePoilicy names) :
        MyMap(CompareNames(names)),
        m_dateTime(dateTime) // store for when inner maps are to be created
    {}

    // a helper function to insert values with a properly initialized comparator
    void add_one(const std::string& key1, const std::string& key2, int val) {
        if(not MyMap.contains(key1)) {
            MyMap.emplace(key1,
                          std::map<std::string, int, CompareDateTime>
                              (CompareDateTime(m_dateTime)));    
        }
        // using std::map::at is one way to avoid the problem:
        MyMap.at(key1).emplace(key2, val);
    }

    void fillMe() {
        // using the helper function:
        add_one("alpha", "1981-08-20 08:05:00", 1);
        add_one("beta", "1981-08-20 08:05:00", 1);
        add_one("gamma", "1981-08-20 08:05:00", 1);
    }

    std::map<std::string,
             std::map<std::string, int, CompareDateTime>, CompareNames> MyMap;
    // to store the comparator argument for the inner maps:
    ComparePoilicy m_dateTime;
};

Demo

Upvotes: 1

Related Questions