views:

87

answers:

3

I need to create a map, from integers to sets of tuples, the tuples in a single set have the same size. The problem is that the size of a tuple and its parameter types can be determined at runtime, not compile time. I am imagining something like:

std::map<int, std::set<boost::tuple> >

but not exctly sure how to exactly do this, bossibly using pointers.

The purpose of this is to create temporary relations (tables), each with a unique identifier (key), maybe you have another approach.

A: 

You can only store these different sets in the same collection if you have some common base class for them. You could write an abstract interface, and then implement that for each kind of table/tuple. The problem is, that usually such interfaces tend to be very messy, and you would possibly have class-explosion if you have many types of table/tuples. Boost.Any could be useful for such an interface (since you have to handle different datatypes dynamically).

Space_C0wb0y
A: 

If the parameter types have something in common, capture that into an abstract base class and make the tuple contain pointers to this base class:

class MyParamBase {
public:
    virtual int getFoo() = 0;
    virtual void setFoo(int) = 0;
};

std::map<int, std::set<MyParamBase*> > container;

(boost::tuple omitted for brevity. Not sure why you need a set of tuples.)

You can then derive the concrete parameter types from MyParamBase and create and insert them into the map:

class SomeParam: MyParamBase {
public:
    virtual int getFoo() { ... }
    virtual void setFoo(int a) { ... }
};

std::set<MyParamBase*> some_set;
some_set.insert(new SomeParam());
container[123] = some_set;

If the parameter types have nothing in common - do not put them into the same map. Quite likely they do not belong together.

digitalarbeiter
+1  A: 

The purpose of boost::tuple is to mix arbitrary types. If, as you say,

I am only inserting integers

then you should use map< int, set< vector< int > > >. (If I were you, I'd throw some typedefs at that.)

To answer the original question, though, boost::tuple doesn't allow arbitrary types at runtime. boost::any does. However, any does not support comparison so there's a little more work if you want to use it in a set.

typedef vector< boost::any > tuple;
struct compare_tuple { bool operator()( tuple const &l, tuple const &r ) const {
    assert ( l.size() == r.size() );

    for ( tuple::iterator lit = l.begin(), rit = r.begin();
          lit != l.end(); ++ lit, ++ rit ) {
        assert ( lit->type() == rit->type() );

        if ( lit->type() == typeid( foo ) ) { // find the type and perform "<"
            return boost::any_cast<foo>(*lit) < boost::any_cast<foo>(*rit);
        } else if ( lit->type() == typeid( bar ) ) {
            return boost::any_cast<bar>(*lit) < boost::any_cast<bar>(*rit);
        } /* etc; you will need to enumerate all the types you can insert */
    }
} };

typedef std::map< int, std::set< tuple, compare_tuple > > main_map;
Potatoswatter