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:
Some background info about OSG to explain the source of my issue:
osg::Box
and osg::Cylinder
are both kinds of osg::Shape
getCenter
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:
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;
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!
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.