0

I want to create a templated API to access derived types that are stored in a vector of vectors. The outer vector has an element for each derived type. The inner vector has a collection of those types.

std::vector<std::vector<MyBaseClass>* > items;

Ideally, I would like to provide an API where derived types of MyBaseClass can be added and accessed (without specialized templates). Something like this:

T& addItem(size_t index, T& item);
T& getItem(size_t index);

Use like this:

AClass : public MyBaseClass {};
BClass : public MyBaseClass {};

addItem<AClass&>(123, item);
addItem<BClass&>(456, item);

AClass& = getItem<AClass&>(123);
BClass& = getItem<BClass&>(456);

The reason for the lack of specialized templates is that I want to enable the use of new derived types without other developers having to modify this code.

So, is there a way I can get this kind of API implemented without having to know the derived class types and specialize the code?

Is it possible to do this with a union and template?

Note: The inner vector needs to store its data consecutively in memory, so I am not using pointers.

Note: I am using C++11 without Boost.

Thanks.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • 4
    A `std::vector` cannot contain objects of derived types. Period. – aschepler Feb 18 '17 at 00:57
  • 4
    When you add objects to `vector `, the vector will only store the base class part of the objects. See [What is object slicing?](http://stackoverflow.com/questions/274626/what-is-object-slicing) – Bo Persson Feb 18 '17 at 00:57
  • You could use a `std::vector>`, and then just have `addItem` perform a cast on the pointer and return a reference. This will, of course, be undefined behavior if you cast to the wrong type and return an invalid reference. – Cornstalks Feb 18 '17 at 01:04
  • How is `addItem` supposed to use `index`? What is it an index into? If you want to create a sparse structure (so you can have an item at index 123 without also having items at indexes 0 through 122), that would appear to contradict "needs to store its data consecutively in memory" requirement. – Igor Tandetnik Feb 18 '17 at 02:50
  • The items would be added consecutively. I have another function that returns a free index. That is, either an previously used index that has become vacant, or the vector size... so you're right about the index, I would hide that in the implementation instead. – user1824607 Feb 18 '17 at 05:51
  • aschepler. Thanks for the feedback. I was wondering about using a templated union, precisely because of object slicing. – user1824607 Feb 21 '17 at 04:21

1 Answers1

0

Something along these lines perhaps:

class FancyStore {
public:
  template <typename T>
  T& addItem(const T& item) {
     void*& raw = store_[typeid(T)];
     if (!raw) {
       raw = new std::vector<T>();
     }
     auto v = static_cast<std::vector<T>*>(raw);
     v->push_back(item);
     return v->back();
  }    

  template <typename T>
  T& getItem(size_t index) {
     void* raw = store_[typeid(T)];
     auto v = static_cast<std::vector<T>*>(raw);
     return (*v)[index];
  }

private:
  std::unordered_map<std::type_index, void*> store_;
};

Live demo. Copying, destructor, error handling are left as an exercise for the reader.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Thank you so much. This looks like what I need. – user1824607 Feb 21 '17 at 04:20
  • Is there a way to resize all of the vectors with a single function call, even though I don't know the types until runtime? e.g. void resizeItemStore(size_t size); – user1824607 Feb 21 '17 at 08:30
  • Can I iterate through store_ and use decltype to call a templated resize function? – user1824607 Feb 21 '17 at 09:00
  • If you need to do something on all vectors generically, then you need a more general type-erasure technique. Something [like this](http://rextester.com/YQJG78758) (the interesting part is the newly introduced `ItemStore` and `ItemStoreImpl`) – Igor Tandetnik Feb 21 '17 at 13:49
  • Yes, of course. That makes total sense. Thank you so much. – user1824607 Feb 22 '17 at 18:53