Question

I'm fairly new to C++, and still trying to get my head around some of the finer points of intermediate-level concepts such as templates/writing generic code. I'm writing an application using OpenSceneGraph (OSG), and basically this is what I'm trying to do:

  • I want to have a general element class that can handle any number of different element 'types'
  • Each instance of the general element class should contain a different shape (depending on the type)
  • The different element types (and the shapes they're mapped to) will only be discovered at run-time, because they're going to depend on source data - e.g. there could be 6 different element types that are all represented by boxes of different sizes. Or there could be 3 different element types - one Box, one Cylinder, one Cone.

Some background info about OSG to explain the source of my issue:

  • osg::Box and osg::Cylinder are both kinds of osg::Shape
  • both derived types have identical methods, getCenter
  • even though you can do osg::Shape myShape = osg::Box(); you can't then say myShape.getCenter(); - doesn't work on osg::Shape objects.

Here's an example of what I'm trying to do:

class MyClass {
private:
    // ???? How to reference 'shape' ??
public:
    MyClass(string _type) {
        // This is for example purposes. Eventually types & mappings will be discovered at run-time.

        if (_type == "FOO") { 
            shape = new osg::Box();
        } else if (_type == "BAR") {
            shape = new osg::Sphere();
        }
    }
    /* 
       ???? How to handle getShape()??
    */
}

int main() {
    string readFromData = "FOO";
    MyClass* myFoo (readFromData);

    string alsoFromData = "BAR";
    MyClass* myBar (alsoFromData);

    osg::Vec3f fooCenter = myFoo->getShape()->getCenter();
    osg::Vec3f barCenter = myBar->getShape()->getCenter();
}

I've tried a few different approaches but haven't quite been able to work it out:

  • creating a MyShape class that extends osg::Shape, and has a virtual function header for getCenter - but this makes MyShape an abstract class that cannot be instantiated.
  • template<typedef T> class MyClass... - but if we only discover the type & shape mappings at runtime, then what goes in the angle brackets throughout the rest of my code? e.g.: MyClass<?????????>* myFoo;
  • using boost::any to store the shape internally - but same issue basically. How do you define a getShape function that could return a pointer to one of several different types?

I can't find any previous questions that deal with this type of scenario specifically (sorry if I missed one!). If anyone can help me it'd be super awesome!

Was it helpful?

Solution

OSG supplies a osg::ShapeVisitor class for situations such as this one. Create a CenterFinderVisitor class that extends osg::ShapeVisitor, overriding each of its virtual member functions to retrieve the center of the corresponding shape. Pass an instance of the CenterFinderVisitor to the osg::ShapeVisitor's accept() member function on the shape instance that you store by pointer inside your class to retrieve the center, like this:

struct CenterFinderVisitor : public osg::ShapeVisitor {
    Vec3 center;
    virtual void    apply (Sphere &s) { center = s.getCenter(); } 
    virtual void    apply (Box &b){ center = b.getCenter(); } 
    // ...and so on for other shapes
};

Now you can implement your getCenter() method as follows:

class MyClass {
private:
    osg::Shape *shape;
public:
    MyClass(string _type) {
        // This is for example purposes. Eventually types & mappings will be discovered at run-time.

        if (_type == "FOO") { 
            shape = new osg::Box();
        } else if (_type == "BAR") {
            shape = new osg::Sphere();
        }
    }
    Vec3 getShapeCenter() {
        CenterFinderVisitor cf;
        shape->accept(cf);
        return cf.center;
    }
};

If you are not familiar with the visitor pattern, read this article on wikipedia.

OTHER TIPS

This is a classic OOP question.

Have shape base class and have all shapes inherit from it. In shape declare all functions (pure virtual or just virtual) you want a shape to have:

class shape {
public:
   shape(string _name) : name(_name) {}
   virtual ~shape(); // virtual desructor
   virtual POINT getCenter() = NULL;
   virtual getName() { return name; } // example - functionality in base class

protected:
   string name;
};

class rectangle : public shape {
{
   rectangle() : shape("rectangle") {}
   virtual POINT getCenter() { return /* do math here :) */ }
};

In your MyClass class, have a pointer/ref to a shape type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top