How to Store Objects of Differing Types in a C++ Container

How can I store objects of differing types in a C++ container?

You could use (or re-implement) boost::any and store instances of boost::any in a container. That would be the safest, since boost::any has probably dealt with much of the edge cases and complexity involved in solving this kind of problem in the general case.

If you want to do something quick and dirty, create a structure or perhaps a union containing members of all potential types along with an enumeration or other indicator of which type is 'active' in the object. Be especially careful with unions as they have some interesting properties (such as invoking undefined behavior if you read the wrong union member, only one of the members can be 'active' at a time, the one that was most recently written to).

I'm curious what you're doing that you need such a construct, though.

How to store functional objects with different signatures in a container?

#include <functional>
#include <iostream>
#include <string>
#include <map>

class api {
// maps containing the different function pointers
typedef void(*voidfuncptr)();
typedef int(*stringcrintptr)(std::string, const int&);

std::map<std::string, voidfuncptr> voida;
std::map<std::string, stringcrintptr> stringcrint;
public:
// api temp class
// given an api and a name, it converts to a function pointer
// depending on parameters used
class apitemp {
const std::string n;
const api* p;
public:
apitemp(const std::string& name, const api* parent)
: n(name), p(parent) {}
operator voidfuncptr()
{ return p->voida.find(n)->second; }
operator stringcrintptr()
{ return p->stringcrint.find(n)->second; }
};

// insertion of new functions into appropriate maps
void insert(const std::string& name, voidfuncptr ptr)
{ voida[name]=ptr; }
void insert(const std::string& name, stringcrintptr ptr)
{ stringcrint[name]=ptr; }
// operator[] for the name gets halfway to the right function
apitemp operator[](std::string n) const
{ return apitemp(n, this); }
};

Usage:

api myMap; 

int hello_world(std::string name, const int & number )
{
name += "!";
std::cout << "Hello, " << name << std::endl;
return number;
}

int main()
{
myMap.insert("my_method_hello", &hello_world );
int a = myMap["my_method_hello"]("Tim", 25);
}

Not very pretty. Better advice is to not do anything even remotely like whatever it is you're trying to do.

Note that this requires all functions with the same parameters to return the same type.

Best practice for storing object in multiple containers

You've seen that you have to store a reference (of some sort) to the object if you want 1 thing to appear in 2 places. Whilst you once would have used a pointer, today we have shared_ptr.

This wraps the pointer so you don't have to manage lifetime ownership- the pointer will only get delete'd when the reference count of the shared_ptr drops to 0, ie when everyone holding the shared_ptr has relinquished it. You use the shared_ptr object as a real object so it's easy to manage in your containers.

If you don't have a C++11 compiler, you can use the boost version (that comes with an example of storing shared_ptr objects in both a vector and a set)

put different class in hierarchy in one container in C++

The problem with vector<vehicle> is that the object only holds vehicles. The problem with vector<vehicle*> is that you need to allocate and, more importantly, free the pointers appropriately.

This might be acceptable, depending on your project, etc...

However, one usually uses some kind of smart-ptr in the vector (vector<boost::shared_ptr<vehicle>> or Qt-something, or one of your own) that handles deallocation, but still permits storing different types objects in the same container.

Update

Some people have, in other answers/comments, also mentioned boost::ptr_vector. That works well as a container-of-ptr's too, and solves the memory deallocation problem by owning all the contained elements. I prefer vector<shared_ptr<T>> as I can then store objects all over the place, and move them using in and out of containers w/o issues. It's a more generic usage model that I've found is easier for me and others to grasp, and applies better to a larger set of problems.

Most efficient way to store reference to container object in each item stored in STL list

You want the most efficient way to store a reference to an object in an object?

The most efficient way is a pointer. It is likely 4 or 8 bytes, and likely doesn't affect the alignment of the next item in the struct, so is therefore fine.

You can save bytes by having numbers that look up owner objects, but this is likely actually not saving real bytes in the struct allocation, its just introducing padding.

Setting a pointer takes a memory word write. The destination is likely adjacent to other writes you are doing during initialization; its unlikely to impact performance even in a tight loop.

An alternative to using stl::list is to put the list nodes themselves inside the data-structure.

This is common in high-performance environments e.g. kernels. Here's a description of the Linux kernel one.

By placing the next (and possibly previous) pointer (or XORing them to save space) inside the struct, no separate memory allocation is required.

This means that a object in the list can only ever be in one list at a time, but your to field means this anyway so this does not constrain you.

You can have a convention that the head is actually the owning object; this obviously takes O(n) to discover but perhaps you need to discover the to only occasionally?

So to summarize:

  • you can save memory by not having an stl::list at all, but rather just have next (and prev, or perhaps XOR them) in the node itself
  • you can use this space you save to add a to field explicitly
  • or you can have a convention where the head of the list is actually the owner

Store derived class objects in base class variables

What you are seeing is Object Slicing.

You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.

Solution:

You should store pointer to object of Base class in the vector:

vector<Base*> 

By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.

Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.

Possibility of store object type for std::any

Is it possible to store the decltype(mySet) as run time variable

No. C++ is a statically typed language. Types cannot be determined at runtime. The type of every expression, like the return value of a function that would return the value stored in the any, must be known at compile time.

That's precisely why you have to supply the type explicitly when you any_cast.



Related Topics



Leave a reply



Submit