Skip to main content
GameDev.net gamedev.net
🔒 Locked

C++ ClassType() ?

Started by fig May 23, 2008 at 7:05 AM 10 replies 1.5k views
Original Post
fig
fig
First of all, I don't know what this is called, so I'm struggling to find info on it. :) I use code similar to below in Delphi to create a new instance of an object, without knowing the actual type of the object. I need to know if there's a way to do it in C++.. In this code, the OtherObj could be any one of the 2 derived classes. Any help would be really appreciated. Even if someone could tell me what it's called? :p
interface

type
    TbaseObjClass = class of TbaseObj;
    TbaseObj = class;

    TDerivedObj1 = class(TbaseObj);
    TDerivedObj2 = class(TbaseObj);

implementation

var
    baseObjClass:TObjClass;
    Obj:TbaseObj;
begin

    baseObjClass:=TbaseObjClass(OtherObj.ClassType);
    Obj:=baseObjClass.create;

end;
summaky
summaky
I think you are looking for "virtual constructors" (look at section 20.8 of CPP FAQ Lite.) At least, if I understood correctly.
superpig
superpig
There's no language-level mechanism for doing what you describe. Given that different classes will have constructors that take different arguments, how would you reconcile that?

However, there are ways of achieving the same result in C++ using factory functions/objects:

class IOtherObjCreator{ public: virtual OtherObj* create() = 0;};class DerivedObj1Creator : IOtherObjCreator{ public : OtherObj* create() { return new DerivedObj1(); }};class DerivedObj2Creator : IOtherObjCreator{ public : OtherObj* create() { return new DerivedObj2(); }};IOtherObjCreator* myObjCreator = GetAppropriateObjectCreatorObject();OtherObj* obj = myObjCreator->create();
Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse
fig
fig
Thanks guys.

Based on the "OtherObjCreator" , I came up with an idea.

I'll have a class manager, which is basically a list of class names with a pointer to an associated class creator function. Each of my classes will be registered with the manager, so I can easily call something like this..

baseObj* obj = ClassManager.CreateObjectFromName('DerivedObj2');

The CreateObjectFromName() function will look up the list of registered classes and then call it's associated creator function.

Each class will also override a GetClassName() function.

The problem with the IOtherObjCreator is that I can't see a way to use it for loading/saving classes to file. eg. to save/load a scene graph. With the class manager idea, I can save/load the class name. When loading, I can read the name and create the object using the class manager.

I think that should work anyway. :)

Feel free to pick holes in the idea if there's anything that might not be possible in C++, I'm still learning..
Rydinare
Rydinare
I've never liked the approach of having a global manager to be able to create a bunch of objects based on a string. I admire the factory pattern and abstract factory patterns because they're structured when used properly. What's the real benefit for being any to create any object based on a string?

In my experience, that global creator becomes a big mess and there winds up being casting back and forth, particularly on the client side to get things to an interface they're comfortable with. I personally think the global creator idea is a bit lazy and makes refactoring hell.

There isn't always a need for any sort of a factory. The factory is mainly useful if the object that will do the creation is going to be passed around. If this is the case, then I have a factory base class to match the hierarchy for the base class interface that will be passed around. No registration or global-ness required. Of course, your mileage may vary.
fig
fig
Quote:
I've never liked the approach of having a global manager to be able to create a bunch of objects based on a string


I'd much rather be able to get the class type straight from the class itself, but.. ;)

Quote:
I personally think the global creator idea is a bit lazy


Only slightly offensive, I wouldn't exactly class myself as a lazy coder. :p

BTW, Delphi uses a manager similar to this for things like image extensions and it works well.
Rydinare
Rydinare
Quote:
Original post by fig
Quote:
I've never liked the approach of having a global manager to be able to create a bunch of objects based on a string


I'd much rather be able to get the class type straight from the class itself, but.. ;)

Quote:
I personally think the global creator idea is a bit lazy


Only slightly offensive, I wouldn't exactly class myself as a lazy coder. :p

BTW, Delphi uses a manager similar to this for things like image extensions and it works well.


Sorry to have phrased it in a way that may have been slightly offensive. So, you stated your preference and ended with but. Can you explain further? [smile]
mikeman
mikeman
Quote:

What's the real benefit for being any to create any object based on a string?


It has its uses. Say you're making a game editor, where you can create entities out of a certain entity gallery by dropping them into the world, like a RAD environment. The editor does not know of any specific concrete class, just the abstract Entity interface. New classes need to be registered to the 'gallery' somehow so they can be used. The user of the editor simply determines which entity class he wants to use by entering its name, or generally an ID, either by simple text or by pressing a toolbar button associated with that ID. Generally, when the concrete type of the object created is determined by user input, you need a factory that can register types using a key, and create an instance of the appropriate type when feeded with that key.
fig
fig
As an example, say I have a tree of objects.

Every object is derived from a cBaseSceneObject. eg. cBox,cMesh,cWhatever. The base object deals with basic stuff like transformations, whereas the derived objects actually define what the object is (visuals, behaviour etc). The tree then will obviously need to store the objects as cBaseSceneObject, because it's the common base class.

If I want to save this tree to file, I can have each object write all it's own data to the file (position,scale etc). So far so good. But how Can I load this tree again without knowing which classes actually saved the data? I can't..

I need to be able to save some sort of class reference to the file, so when I read the reference back, I can create a cBox, for example, and then call cBox.load() to tell the new box to load it's data from the file.

This is where the manager comes in. If I store the class name in the file, I can then create the class from the name, then load the data.

The saving/loading is the main reason I need this, but there will be other situations where it'll be useful too.

Would I be able to use the "IOtherObjCreator" suggestion for this and I just can't see how? :)
Rydinare
Rydinare
Quote:
Original post by mikeman
Quote:

What's the real benefit for being any to create any object based on a string?


It has its uses. Say you're making a game editor, where you can create entities out of a certain entity gallery by dropping them into the world, like a RAD environment. The editor does not know of any specific concrete class, just the abstract Entity interface. New classes need to be registered to the 'gallery' somehow so they can be used. The user of the editor simply determines which entity class he wants to use by entering its name, or generally an ID, either by simple text or by pressing a toolbar button associated with that ID. Generally, when the concrete type of the object created is determined by user input, you need a factory that can register types using a key, and create an instance of the appropriate type when feeded with that key.


Ahh, I understand for external tools or possibly to interface between two languages that there can be a use. Although, even then, I would probably have an interface which mapped id to factory instead of id to object.

What I don't get, though, is if you're purely creating things internally in C++ (or Java or whatever language), why would you want a global factory based on a string id?
mikeman
mikeman
Quote:

What I don't get, though, is if you're purely creating things internally in C++ (or Java or whatever language), why would you want a global factory based on a string id?


One other important use is, as fig says, serialization. Say you have a World instance that you want to save to a file, and then load it again. That World instance has a list of pointers to Entity*. Those are polymorphic, meaning that each Entity* could point to a different concrete type(like Player,Enemy,Weapon...). How would you reconstruct the objects when you don't know each Entity's class at compile-time? Simply, you map each class to an ID(string or otherwise), save that ID to the file, and when loading, you use that ID to find the class of this instance, and reconstruct the object. Or, you can use boost::serialization, which handles all this the mapping and reconstruction for you. Fig, take a look at boost::serialization, I think it would solve the problem you mention.
superpig
superpig
You don't actually want a factory that can create an instance of any class - for example, you're not interested in having it create vectors or iostreams. In truth you only want to create a particular set of classes, and they're all classes that have a certain property in common: they're all classes you want to include in the savegame.
Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.