views:

102

answers:

4

Hello!

How should the following cases be handled:

I have some geometrical storage, which is a template from vertex type.

template <typename T> struct Geometry {

std::vector<T>& GetVertices() { ... }

const void* RawVertices() const { ... }

}

This works fine, unless I want to store different types of geometries (for instance, Geometry<Vertex1> g1 and Geometry<Vertex2> g2 in one container.

Is this possible?

Or how should I implement geometry storage (where I can store and retrieve different types of geometries using one container) or maybe somehow map T type to Geometry<T> type?

Any advices?

Thank you.

+2  A: 

Since a container is tied to one type of data it can contain, you could create a class GeometryBase from which all Geometry<T> are derived and then store GeometryBase pointers in your container.

struct GeometryBase
{
    // Non-template methods might go here.
    // Don't forget to declare the base class destructor virtual.
};

template <typename T> struct Geometry : public GeometryBase
{
    // Template methods go here
};

Edit:
At some point you will have to decide which type of vertex container you want to get (my approach) or what you want to do with a vertex (Vijay Mathew's approach) and then you'll have to dynamic_cast<> in order to get access to the derived class methods.

Another suggestion:
If the types are as different as your describe in your comments, it might actually be better to treat them as different types.
For example, you could create a separate container for each Geometry<> template instance.

class SomeStorageClass
{
/* ... */
private:
    std::vector< Geometry<Vertex1> > m_vertex1Geometries;
    std::vector< Geometry<Vertex2> > m_vertex2Geometries;
};

If you have functions that operate on one kind of geometry (using Vertex1::GetPos(), to use your example) or the other (Vertex2::GetUV()) then these functions are probably implemented quite differently and thus deserve to be separate functions expecting diferent types of parameters.

mxp
Remember to put a virtual destructor :)
Vincent Robert
And how should I implement those `GetVertices`-like accessors if I choose your way?
HardCoder1986
HardCoder1986
+1  A: 

Heterogeneous containers (i.e., that store more than one type of object) introduce quite a few problems -- for an obvious example, when you retrieve an object, you have to do something to figure out which kind of object you're retrieving.

The containers built into C++ take the simple route: they're homogeneous -- they only store one type of object. If you want to store objects of two different types, it's up to you to wrap both of those into some third type that you store in the container (usually along with something to indicate which type a particular object really is). For example, you could do something like this:

class vertex1 {};

class vertex2 {};

class vertex {
    vertex1 *v1;
    vertex2 *v2;
public:
    vertex(vertex1 *init1) : v1(init1), v2(NULL) {}
    vertex(vertex2 *init2) : v1(NULL), v2(init2) {}
};

std::vector<vertex> vertices;

Of course, there are lots of variations (e.g., storing a pointer to a base class), but in the end it comes down to one thing: the collection itself holding one type of object, and that type somehow or other managing the two other types you need to deal with.

Jerry Coffin
Vijay Mathew
@Vijay: no, not in this case. Instead of creating a container of pointers, we're creating a container of objects that themselves contain a couple of pointers. I'm reasonably certain containers of references are not allowed.
Jerry Coffin
+2  A: 

As GetVertices will only return objects of type Vertex, I suggest you move to an Object Oriented design from generics.

class Vertex
{
   ....
};

class Vertex1 : public Vertex 
{
   ....
};

class Vertex2 : public Vertex 
{
   ....
};

typedef std::vector<Vertex*> Vertices;

struct Geometry
{
    const Vertices& GetVertices() const { .... }
    ....
};
Vijay Mathew
I guess that your sample fails if I want `Vertex1` to have members like `GetPos()` and `GetNormal()` and `Vertex2` to also introduce `GetUV()` method, which can't be added to base superclass.
HardCoder1986
@HardCoder1986: It still works, you'll just have to down-cast into the appropriate type at run-time.
tzaman
@HardCoder1986 dynamic_cast. Moreover, that problem was not part of the original question. My answer shows the common C++ idiom for dealing with a situation like the one described.
Vijay Mathew
@tzaman And how should I determine the type that I should cast into? (except the fact that downcasting isn't very cool :)
HardCoder1986
@HardCoder1986: Might not be cool, but it's necessary if you want to get differently typed objects out of a homogeneous container.
tzaman
+1  A: 
class IGeometry
{
public:
    virtual const void* RawVertices() const = 0;
    virtual ~IGeometry() {}

    template <typename T>
    std::vector<T>& GetVertices() const 
    { 
        typedef const Geometry<T>* AppropriateDerivedClass;
        return dynamic_cast<AppropriateDerivedClass>(this)->GetVertices();
    };
};
Grozz