how do you make a heterogeneous boost::map?

21,909

Solution 1

#include <map>
#include <string>
#include <iostream>
#include <boost/any.hpp>

int main()
{
    try
    {
        std::map<std::string, boost::any> m;
        m["a"]  = 2;
        m["b"]  = static_cast<char const *>("black sheep");

        int i = boost::any_cast<int>(m["a"]);
        std::cout << "I(" << i << ")\n";

        int j = boost::any_cast<int>(m["b"]); // throws exception
        std::cout << "J(" << j << ")\n";
    }
    catch(...)
    {
        std::cout << "Exception\n";
    }

}

Solution 2

How can I build a <favorite container> of objects of different types?

You can't, but you can fake it pretty well. In C/C++ all arrays are homogeneous (i.e., the elements are all the same type). However, with an extra layer of indirection you can give the appearance of a heterogeneous container (a heterogeneous container is a container where the contained objects are of different types).

There are two cases with heterogeneous containers.

The first case occurs when all objects you want to store in a container are publicly derived from a common base class. [...]

The second case occurs when the object types are disjoint — they do not share a common base class.
The approach here is to use a handle class. The container is a container of handle objects (by value or by pointer, your choice; by value is easier). Each handle object knows how to "hold on to" (i.e., maintain a pointer to) one of the objects you want to put in the container. You can use either a single handle class with several different types of pointers as instance data, or a hierarchy of handle classes that shadow the various types you wish to contain (requires the container be of handle base class pointers). The downside of this approach is that it opens up the handle class(es) to maintenance every time you change the set of types that can be contained. The benefit is that you can use the handle class(es) to encapsulate most of the ugliness of memory management and object lifetime. Thus using handle objects may be beneficial even in the first case.

Solution 3

Would boost::any do the trick for you?

Solution 4

Thank you David, that was what I needed. Here's the working solution.

#include <iostream>
using std::cout;
using std::endl;

#include <map>
#include <boost/any.hpp>

using boost::any_cast;
typedef std::map<std::string, boost::any> t_map;


int main(int argc, char **argv)
{

  t_map map;
  char *pc = "boo yeah!";

  map["a"] = 2.1;
  map["b"] = pc;

  cout << "map contents" << endl;
  cout << any_cast<double>(map["a"]) << endl;
  cout << any_cast<char*>(map["b"]) << endl;

  return 0;
}

Solution 5

If you want a limited set of types to be supported, Boost.Variant should do the trick.

Share:
21,909
Kurt
Author by

Kurt

C++ and Python developer, Linux by choice.

Updated on May 15, 2020

Comments

  • Kurt
    Kurt almost 4 years

    I want to have a map that has a homogeneous key type but heterogeneous data types.

    I want to be able to do something like (pseudo-code):

    boost::map<std::string, magic_goes_here> m;
    m.add<int>("a", 2);
    m.add<std::string>("b", "black sheep");
    
    int i = m.get<int>("a");
    int j = m.get<int>("b"); // error!
    

    I could have a pointer to a base class as the data type but would rather not.

    I've never used boost before but have looked at the fusion library but can't figure out what I need to do.

    Thanks for your help.