Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Store multiple types as values in C++ dictionary?

Tags:

c++

python

I want to write a C++ object that behaves almost equivalent to a Python dictionary. C++'s std::map and std::unordered_map accommodate some of the functionalities that Python dictionaries already have, yet lack one of the most important capabilities, namely, being able to add arbitrary objects and types. Even if it isn't possible, how close can you get to achieving the capabilities of Python's dictionary?

A few previous questions (here and here) fail to handle the issue of adding multiple types to the dictionary.

For example, I want to be able to do something like this in C++:

my_dict = {'first': 1,
           'second': "string",
           'third': 3.5,
           'fourth': my_object}
my_dict['fifth'] = list([1, 2, 3])

The best solutions that I can think would be like using void pointers to data which get reinterpreted, or perhaps some kind of run-time polymorphism with some type restrictions?

like image 419
user5915738 Avatar asked May 28 '19 22:05

user5915738


People also ask

Can dictionary have multiple data types?

One can only put one type of object into a dictionary. If one wants to put a variety of types of data into the same dictionary, e.g. for configuration information or other common data stores, the superclass of all possible held data types must be used to define the dictionary.

Can dictionary store multiple values?

In python, if we want a dictionary in which one key has multiple values, then we need to associate an object with each key as value. This value object should be capable of having various values inside it. We can either use a tuple or a list as a value in the dictionary to associate multiple values with a key.

Can a key in a dictionary have multiple values in C#?

Note: If you have multiple values acting as the key, you would define a class to encapsulate those values and provide proper overrides of GetHashCode and Equals so that the dictionary could recognize their equality.

Can we store anything other than string or INT in dictionary key?

You can use a Dictionary<string,object> and then you can put anything you want into it.


2 Answers

The best solutions that I can think would be like using void pointers to data which get reinterpreted, or perhaps some kind of run-time polymorphism with some type restrictions?

Many void pointers and many reinterpretation of pointers in modern C++ should be a signal of a bad design, in my opinion. I believe that polymorphism would be a way to go.

Also, if you have a fixed number of types you want to use, consider using C++17's std::any or std::variant which is a more modern union.

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

typedef std::map<std::variant<int, std::string>, std::variant<int, std::string>> Dict;

int main(){
    Dict d;
    d["key"] = 1;
    d[5] = "woah";
    std::cout << std::get<int>(d["key"]) << std::endl; // prints 1

    // edit: print all the keys in d
    for(auto& k_v: d){
        std::visit(
            [](auto& value){std::cout << value << ' ';},
            k_v.first // k_v.second to print all values in d
        );
    }

}

The above example might be useful in some use-cases.

Also check out how json objects are implemented in any C++ json library. It might be helpful.

Edit: I added an example of std::visit that iterates over keys in our dictionary.

like image 169
asikorski Avatar answered Oct 19 '22 21:10

asikorski


I was looking for a similar solution for hard coding the parameters of my python experiments into c++. I found the std'17 variant module very useful.

#include <iostream>
#include <map>
#include <variant>
#include <vector>
#include <string>

int main(){
    typedef std::map<std::variant<std::string, int>, std::variant<int, double, float, std::string>> Dict;

    std::vector<Dict> params = {
        {
            {"label", "Labs"},
            {"dir", "/media/sf_Data/"},
            {"num_frames", 4},
            {"scale_factor", 1.0},
            {5, "my number five"},
        }, // Dict 0
        {
            {"label", "Airplanes"},
            {"dir", "/media/m_result/"},
            {"num_frames", 5},
            {"scale_factor", 0.5},
            {5, "number five"},
        } // Dict 1
    };

    int idx = 1;
    std::string label   = std::get<std::string>(params[idx]["label"]);
    std::string folder  = std::get<std::string>(params[idx]["dir"]);
    int num_frames      = std::get<int>(params[idx]["num_frames"]);
    double scf          = std::get<double>(params[idx]["scale_factor"]);
    std::string nbstr   = std::get<std::string>(params[idx][5]);
    
    std::cout << label << std::endl;
    std::cout << folder << std::endl;
    std::cout << num_frames << std::endl;
    std::cout << scf << std::endl;
    std::cout << nbstr << std::endl;

    return 0;
}

The result:

Airplanes
/media/m_result/
5
0.5
number five
like image 25
Erman Avatar answered Oct 19 '22 21:10

Erman