views:

1106

answers:

8

It looks like I had a fundamental misunderstanding about C++ :<

I like the polymorphic container solution. Thank you SO, for bringing that to my attention :)


So, we have a need to create a relatively generic container type object. It also happens to encapsulate some business related logic. However, we need to store essentially arbitrary data in this container - everything from primitive data types to complex classes.

Thus, one would immediately jump to the idea of a template class and be done with it. However, I have noticed C++ polymorphism and templates do not play well together. Being that there is some complex logic that we are going to have to work, I would rather just stick with either templates OR polymorphism, and not try to fight C++ by making it do both.

Finally, given that I want to do one or the other, I would prefer polymorphism. I find it much easier to represent constraints like "this container contains Comparable types" - a la java.

Bringing me to the topic of question: At the most abstract, I imagine that I could have a "Container" pure virtual interface that has something akin to "push(void* data) and pop(void* data)" (for the record, I am not actually trying to implement a stack).

However, I don't really like void* at the top level, not to mention the signature is going to change every time I want to add a constraint to the type of data a concrete container can work with.

Summarizing: We have relatively complex containers that have various ways to retrieve elements. We want to be able to vary the constraints on the elements that can go into the containers. Elements should work with multiple kinds of containers (so long as they meet the constraints of that particular container).

Edit: I should also mention that the containers themselves need to be polymorphic. That is my primary reason for not wanting to use templated C++.

So - should I drop my love for Java type interfaces and go with templates? Should I use void* and statically cast everything? Or should I go with an empty class definition "Element" that declares nothing and use that as my top level class in the "Element" hierarchy?

One of the reasons why I love stack overflow is that many of the responses provide some interesting insight on other approaches that I hadn't not have even considered. So thank you in advance for your insights and comments.

+4  A: 

Polymorphism and templates do play very well together, if you use them correctly.

Anyway, I understand that you want to store only one type of objects in each container instance. If so, use templates. This will prevent you from storing the wrong object type by mistake.

As for container interfaces: Depending on your design, maybe you'll be able to make them templated, too, and then they'll have methods like void push(T* new_element). Think of what you'll know about the object when you want to add it to a container (of an unknown type). Where will the object come from in the first place? A function that returns void*? Do you know that it'll be Comparable? At least, if all stored object classes are defined in your code, you can make them all inherit from a common ancestor, say, Storable, and use Storable* instead of void*.

Now if you see that objects will always be added to a container by a method like void push(Storable* new_element), then really there will be no added value in making the container a template. But then you'll know it should store Storables.

Lev
Although this could be wrong - i think it is safe to assume that yes, each container will store an element of one type.However, I have a need for the containers themselves to be polymorphic. That is what I find difficult about templates.
Voltaire
"if you use them correctly" = downfall of many allegedly good ideas.
DarenW
+5  A: 

The simple thing is to define an abstract base class called Container, and subclass it for each kind of item you may wish to store. Then you can use any standard collection class (std::vector, std::list, etc.) to store pointers to Container. Keep in mind, that since you would be storing pointers, you would have to handle their allocation/deallocation.

However, the fact that you need a single collection to store objects of such wildly different types is an indication that something may be wrong with the design of your application. It may be better to revisit the business logic before you implement this super-generic container.

Dima
+12  A: 

You can look at using a standard container of boost::any if you are storing truly arbitrary data into the container.

It sounds more like you would rather have something like a boost::ptr_container where anything that can be stored in the container has to derive from some base type, and the container itself can only give you reference's to the base type.

Greg Rogers
A: 

Using polymorphism, you are basically left with a base class for the container, and derived classes for the data types. The base class/derived classes can have as many virtual functions as you need, in both directions.

Of course, this would mean that you would need to wrap the primitive data types in derived classes as well. If you would reconsider the use of templates overall, this is where I would use the templates. Make one derived class from the base which is a template, and use that for the primitive data types (and others where you don't need any more functionality than is provided by the template).

Don't forget that you might make your life easier by typedefs for each of the templated types -- especially if you later need to turn one of them into a class.

Caleb Huitt - cjhuitt
+2  A: 

First, of all, templates and polymorphism are orthogonal concepts and they do play well together. Next, why do you want a specific data structure? What about the STL or boost data structures (specifically pointer containter) doesn't work for you.

Given your question, it sounds like you would be misusing inheritance in your situation. It's possible to create "constraints" on what goes in your containers, especially if you are using templates. Those constraints can go beyond what your compiler and linker will give you. It's actually more awkward to that sort of thing with inheritance and errors are more likely left for run time.

David Nehme
The container itself has interesting properties independent of the data it stores. And it would be nice if it was polymorphic. There might be an Open Source library that is near to what we need, but I doubt it fits exactly our requirements.
Voltaire
+2  A: 

Can you not have a root Container class that contains elements:

template <typename T>
class Container
{
public: 

   // You'll likely want to use shared_ptr<T> instead.
   virtual void push(T *element) = 0;
   virtual T *pop() = 0;
   virtual void InvokeSomeMethodOnAllItems() = 0;
};

template <typename T>
class List : public Container<T>
{
    iterator begin();
    iterator end();
public:
    virtual void push(T *element) {...}
    virtual T* pop() { ... }
    virtual void InvokeSomeMethodOnAllItems() 
    {
       for(iterator currItem = begin(); currItem != end(); ++currItem)
       {
           T* item = *currItem;
           item->SomeMethod();
       }
    }
};

These containers can then be passed around polymorphically:

class Item
{
public:
   virtual void SomeMethod() = 0;
};

class ConcreteItem
{
public:
    virtual void SomeMethod() 
    {
        // Do something
    }
};  

void AddItemToContainer(Container<Item> &container, Item *item)
{
   container.push(item);
}

...

List<Item> listInstance;
AddItemToContainer(listInstance, new ConcreteItem());
listInstance.InvokeSomeMethodOnAllItems();

This gives you the Container interface in a type-safe generic way.

If you want to add constraints to the type of elements that can be contained, you can do something like this:

class Item
{
public:
  virtual void SomeMethod() = 0;
  typedef int CanBeContainedInList;
};

template <typename T>
class List : public Container<T>
{
   typedef typename T::CanBeContainedInList ListGuard;
   // ... as before
};
Eclipse
That is what I was thinking. The only issue is that Item is essentially an empty class. As far as I can tell, I can place no restrictions on what specifically is stored in the container, thus there really isn't a common interface I can extract out. But perhaps that isn't so bad.
Voltaire
Also.. I don't think you can have virtual functions in template.Another major problem ;)
Voltaire
You can have virtual functions in a template.
Lev
Ah. I don't know why I thought you couldn't - im going to go test some stuff. Might be a polymorphic template class would do everything I want, if I can create such a thing.
Voltaire
I just tested my example, and AddItemToContainer invokes the virtual call to List<Item>::push.
Eclipse
Note that Item isn't a class, its a template parameter, so any class that works with Container will work.
KeithB
Also, that is a really neat trick with the ListGaurd.
Voltaire
A: 

You might also want to check out The Boost Concept Check Library (BCCL) which is designed to provide constraints on the template parameters of templated classes, your containers in this case.

And just to reiterate what others have said, I've never had a problem mixing polymorphism and templates, and I've done some fairly complex stuff with them.

KeithB
A: 

You could not have to give up Java-like interfaces and use templates as well. Josh's suggestion of a generic base template Container would certainly allow you do polymorphically pass Containers and their children around, but additionally you could certainly implement interfaces as abstract classes to be the contained items. There's no reason you couldn't create an abstract IComparable class as you suggested, such that you could have a polymorphic function as follows:

class Whatever
{
   void MyPolymorphicMethod(Container<IComparable*> &listOfComparables);
}

This method can now take any child of Container that contains any class implementing IComparable, so it would be extremely flexible.

Reddog