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
andosg::Cylinder
are both kinds ofosg::Shape
- both derived types have identical methods,
getCenter
- even though you can do
osg::Shape myShape = osg::Box();
you can't then saymyShape.getCenter();
- doesn't work onosg::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 extendsosg::Shape
, and has a virtual function header forgetCenter
- but this makesMyShape
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!
OSG supplies a
osg::ShapeVisitor
class for situations such as this one. Create aCenterFinderVisitor
class that extendsosg::ShapeVisitor
, overriding each of its virtual member functions to retrieve the center of the corresponding shape. Pass an instance of theCenterFinderVisitor
to theosg::ShapeVisitor
'saccept()
member function on the shape instance that you store by pointer inside your class to retrieve the center, like this:Now you can implement your
getCenter()
method as follows:If you are not familiar with the visitor pattern, read this article on wikipedia.