0

Storing items of the same type is trivial, but I need a container that can store items of different types.

Here's an example showing what I'd like to do:

Class C
{
};

C c1;
C c2;
C c3;

std::tuple<C> tup1(c1);
std::tuple<C, C> tup2(c1, c2);
std::tuple<C, C, C> tup3(c1, c2, c3);

container_type container(tup1, tup2, tup3);

Is there any container that works in this way? If not, is there a way to create one?

I'd like something that overloads operator[] for fast random access.

Michael Gazonda
  • 2,720
  • 1
  • 17
  • 33
Ivan
  • 7,448
  • 14
  • 69
  • 134
  • What's wrong with a plain tuple ? – Quentin Feb 21 '15 at 03:43
  • Store what exactly? Your example shows three `C`'s and three tuples of `C`'s. – Pradhan Feb 21 '15 at 03:44
  • See the revised question. I need to store tup1, tup2, or tup3 in the same container. So what is the syntax in std::vector> ? – Ivan Feb 21 '15 at 03:49
  • Ok, that helps. Another question - is there any relation at all between `tup1`, `tup2` and `tup3`? Your example shows a very strong relationship. If you are trying to store prefixes, the problem becomes trivial. So getting that out of the way. – Pradhan Feb 21 '15 at 03:50
  • They __all__ have elements of type C. It is because the tuple is variadic that it is giving me trouble. I have a function that returns a variadic tuple and it needs to go into a container. – Ivan Feb 21 '15 at 03:51
  • A `vector` is a type-homogenous container. `tuple` and `tuple` are different types and can't be held in a `vector` without some type-erasure. Why not use a `vector>`? – Pradhan Feb 21 '15 at 03:54
  • strong typing sometimes really sucks. They are all identical except the number of times C appears in the tuple. I can't really use a tuple to store the tuples because I can't index into a tuple fast. I need a container that has operator[]. Or at worst a hash. – Ivan Feb 21 '15 at 03:57
  • @Ivan May I ask why you return compile-time sized `tuple`s in the first place, instead of `vector`s ? Variadic templates seem to be the wrong tool for what you're trying to do. – Quentin Feb 21 '15 at 04:06
  • Because I have an API that is already in place with variadic templates :( – Ivan Feb 21 '15 at 04:08
  • This seems close http://stackoverflow.com/questions/20701397/how-can-i-use-c11-variadic-templates-to-define-a-vector-of-tuples-backed-by-a?rq=1 – Ivan Feb 21 '15 at 04:09
  • Maybe [this one](http://stackoverflow.com/questions/27790038/c-feed-template-function-element-from-tuple-at-runtime/27790304#27790304) can help you. This may get a bit convoluted though. – Quentin Feb 21 '15 at 04:18
  • There can't be a `operator[]` that returns different types depending on the index. The index is a runtime variable and the return type is a compile time type. Either use `std::get(tup)` on a `std::tuple` and iterate over it at compile time, or use `std::vector` instead of the `std::tuple`. – tmlen Feb 21 '15 at 12:06
  • tmlen the problem is that I am iterating while adding the items to the container. AFAIK, you have to know all the tuple items when you say make_tuple. There is no "push_back" for tuples. There is probably a way to hack it with tuple_cat and continously creating a new tuple from the old one... – Ivan Feb 21 '15 at 15:24
  • tmlen, the more I think about it, the more the code I need is almost identical to your solution here!! http://stackoverflow.com/questions/28637654/turning-a-bounded-stdlistclass-of-parameters-into-a-type-stdtupleclass-cl – Ivan Feb 21 '15 at 15:58

3 Answers3

1

It won't work. You are mixing compile-time polymorphism (the element types and count of a tuple) with run-time polymorphism (you want to store differently sized tuples in a container).

In your example, what should container[0].get<2> return? Tuples are guaranteed to be compile-time checked, so there is no runtime information to check whether 2 is a valid index, so throwing an exception or returning a default constructed object can't happen just from using the tuple.

In your example, you are going to copy or move the tuple (when inserting them into the container) anyway, which means you need to copy or move all the elements. You could instead of copying the tuples elements into (new) tuples copy the tuple elements into std::vectors. The function tuple_to_vector would need some template magic to implement efficiently, which I don't have the time to draft up right now.

If you can change the API to return std::arrays instead of std::tuples (which actually guarantee homogenous type and contiguous storage, and provide run-time indexing), a convert-to-vector function would be quite trivial to write.

Michael Karcher
  • 3,803
  • 1
  • 14
  • 25
1

Run-time polymorphism requires indirection and polymorphic types. What tuples, by their nature, aren't.

What you can do is make your objects part of an inheritance hierarchy, and store in the container (smart)pointers to dynamically allocated objects (dynamic allocation is necessary, since being objects of different types they also have different sizes), or wrap your objects into a class (like boost::any, for example) that does "type cancellation", essentially by doing internally the indirection required).

In any case, the container interface, when doing this, will access the pointers or the wrappers. It's up to you to check at runtime the actual type of each referred object before accessing its own specific functions, or to rely only to the virtual function that are common to all.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
0

Something like:

auto tuple = std::make_tuple(1, "foo", 3.12);
auto foo = std::get<1>(tuple); // will be a string containing "foo"

?

vincentp
  • 1,433
  • 9
  • 12
  • far more complicated than this. I have to use a variadic tuple and std::initializer_list and then continously use tuple_cat – Ivan Feb 21 '15 at 15:47