Option 1, dynamic cast.
class Object {};
class Box : public Object {};
class Sphere : public Object {};
void f(Object* obj)
{
if(Sphere* sphere = dynamic_cast<Sphere*>(obj))
{
// do something with sphere
}
if(Box* box = dynamic_cast<Box*>(obj))
{
// do something with box
}
}
Option 2, roll your own form of RTTI:
#include <iostream>
#include <vector>
#include <cstdint>
#define DECLARE_TYPE(TypeId, ObjectType, Base) \
bool is(uint32_t typeId) const override { \
if(typeId == (TypeId)) return true; \
return Base::is(typeId); \
} \
constexpr static uint32_t kTypeId = (TypeId); \
const char* typeName() const override { return #ObjectType; } \
uint32_t typeId() const override { return kTypeId; }
class Object
{
public:
constexpr static uint32_t kTypeId = 0;
template<typename T>
T* as() const
{
return is(T::kTypeId) ? (T*)this : nullptr;
}
virtual bool is(uint32_t typeId) const
{
return typeId == kTypeId;
}
virtual const char* typeName() const
{
return "Object";
}
virtual uint32_t typeId() const
{
return kTypeId;
}
};
class Box : public Object
{
public:
DECLARE_TYPE(1, Box, Object);
};
class Sphere : public Object
{
public:
DECLARE_TYPE(2, Sphere, Object);
};
int main()
{
std::vector<Object*> objs;
objs.emplace_back(new Box);
objs.emplace_back(new Sphere);
objs.emplace_back(new Object);
for(auto obj : objs)
{
std::cout << obj->typeName() << std::endl;
std::cout << obj->typeId() << std::endl;
Box* b = obj->as<Box>();
std::cout << "is box? " << (b ? "Yes" : "No") << std::endl;
Sphere* s = obj->as<Sphere>();
std::cout << "is sphere? " << (s ? "Yes" : "No") << std::endl << std::endl;
}
for(auto obj : objs)
delete obj;
return 0;
}
My advice however, would be to rethink your design if possible, so that you start thinking about collections of objects, rather than single objects
class ObjectCollection
{
virtual void drawAll();
virtual void updateAll();
};
class BoxCollection : public ObjectCollection {};
class SphereCollection : public ObjectCollection {};
std::vector<ObjectCollection*> thingsInMyGame;
When you are doing things in OpenGL, you generally want to minimise the amount of state changes needed during your render loop, and if possible, leverage hardware instancing. This is significantly easier if you have already grouped your game objects by type. Retroactively trying to optimise a std::vector<Object*>
for OpenGL rendering won't be very successful.