0

I'm working on some 3D rending using OpenGL and I have made a base class object, which should be used for any type of object that can be rendered. I'm storing all my objects in a vector of Object Pointers.

Because different objects may require different treatment by some functions, it would be handy to know, if a pointer points to a for example cube or a sphere.

So is there a way in C++ to get the type of a variable when a pointer to this variable is known?

user11914177
  • 885
  • 11
  • 33
  • 2
    Depends. Can you give a more concrete example of what you want to do? – NathanOliver Jan 02 '20 at 15:27
  • @NathanOliver i did – user11914177 Jan 02 '20 at 15:31
  • The common solution to the problem you have described is to use polymorphism using virtual member functions. Then you can do `object_vector[some_index]->virtual_function_name()` and `virtual_function_name` will call the function derived class version of the function instead of the base class as long as it is marked `virtual` in the base class. – NathanOliver Jan 02 '20 at 15:33
  • If your type hierarchy is of your own making, this sounds like you you just need a few polymorphic methods to do whatever actions you need to customize....? You can implement default methods in your hierarchy's base class, and override them polymorphically in only those derived classes that need special behavior. – phonetagger Jan 02 '20 at 15:34
  • @NathanOliver no I don't mean that. I mean if some external function is going over my vector of objects and does something to it, then I want to do something different for a different type. – user11914177 Jan 02 '20 at 15:35
  • Yes... NathanOliver is exactly right. You are describing a perfect case where you just need polymorphism. – phonetagger Jan 02 '20 at 15:36
  • @user11914177 You are storing pointers to derived objects in the vector, right? – NathanOliver Jan 02 '20 at 15:36
  • @NathanOliver I'm storing my derived objects in a vector of base class pointers – user11914177 Jan 02 '20 at 15:37
  • 2
    As your "external function" is looping over the vector of these objects, it just calls a polymorphic method to do whatever needs done (or make a decision returning true/false), and for most instances the default implementation works fine. In only those special classes that need alternate behavior, you override that method and it does something different in only those classes. – phonetagger Jan 02 '20 at 15:38
  • 1
    These base class pointers... are they of a type that you created? Your post says "I have made a base class `object`..." so I'm assuming you created the base class. – phonetagger Jan 02 '20 at 15:39
  • 1
    Right. Because you have a pointer to the base class, any static type analysis is only going to give you the base class as the type the pointer points to no matter what it actually points to. You either need polymorphism or some other technique like that like using a `variant`/`any` and using the visitor pattern. – NathanOliver Jan 02 '20 at 15:40
  • 1
    Using polymorphism, when you call a polymorphic method via that pointer, you get the derived class's instance of that method, which seems to be exactly what you need. The code looping over the vector doesn't even need to know what each element's type is; it just calls the polymorphic method. – phonetagger Jan 02 '20 at 15:42

4 Answers4

1

The best solution to your use case seems to be the visitor pattern.

Visitor Pattern Explanation

https://en.wikipedia.org/wiki/Visitor_pattern

The pattern allows you to implement type-specific behavior without changing the implementation of these types (except extending it once of course).

lars
  • 475
  • 2
  • 6
0

Long story short: No.

If you own a typed pointer (e.g. char *) - well, that might be a char what the pointer points, but we will never know the truth. If you own a void * pointer, then good luck, you aren't going to find anything interesting.

The types don't exist at runtime. Size is preserved (int = 4 bytes, double = 8 bytes), but no type info is usually guaranteed to be stored.

Kamila Szewczyk
  • 1,874
  • 1
  • 16
  • 33
  • I'd like to know how you can divine the size of the object to which a `void*` points without already knowing its type. – phonetagger Jan 02 '20 at 15:32
  • 1
    you can't. Size is preserved for variables. Pointer is a variable and you can deduce the size of `void *` by arcane magic called `sizeof`. – Kamila Szewczyk Jan 02 '20 at 15:35
  • You say "Size is preserved (int = 4 bytes, double = 8 bytes), but no type info is usually guaranteed to be stored". So I'm asking how you can get this preserved size information about an object at runtime without knowing its type, given only a `void*` to it. I already know the answer, and it's not `sizeof(void*)`. I'm just checking to see if you know the answer. – phonetagger Jan 02 '20 at 16:04
  • You misunderstood my answer. Size is preserved for example for variables, because the code actually refers to it while reading or writing the stack. No type data is known at runtime. I didn't say, that I know how to check size of an object at runtime while only having a void pointer to it. – Kamila Szewczyk Jan 02 '20 at 16:51
  • **If you own a void * pointer, then good luck, you aren't going to find anything interesting.** <- there you go, an answer in an answer. – Kamila Szewczyk Jan 02 '20 at 16:52
  • Well then, if you have a variable itself (not a `void*` to it), you're correct that you can get the size of it by `sizeof(yourVariable)`, but that's resolved at compile time, not runtime. And if you have a vector of base class pointers to derived implementations of differing sizes, `sizeof(*vectOfBaseClassPointers[n])` will give you the size of a base class object (if it is instantiable), not the size of the actual object pointed to by `vectOfBaseClassPointers[n]`. You can't get the size of a derived class object at runtime if you don't have a way to determine its actual instantiated type. – phonetagger Jan 02 '20 at 17:21
0

So it seems that there is no real solution to this other than using virtual functions.

user11914177
  • 885
  • 11
  • 33
  • Not exactly the *only* solution... but I think virtual functions are the **best** solution given what you've described. You could also store an enumerated value as a member variable (defined in the base class) so you can do a `switch(m_objectTypeCode)` on it in your loop, or use RTTI (`type_info` and `typeid()`) to do it slightly more elegantly. Or perhaps use the visitor pattern as lars suggests. All of those solutions would actually be more complicated than simply using virtual methods. – phonetagger Jan 02 '20 at 18:05
0

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.

robthebloke
  • 9,331
  • 9
  • 12