GP2X General C++ Question, Class Type Variable?


Daid

Member
Joined
Jun 13, 2006
Messages
267
Location
Netherlands
Website
Visit site
I found it impossible to google into this. And so far no tutorial I've found covers it.

It's something I've used in UnrealScript (Unreal's own scripting language) alot. A class type variable.

I'll show an example: (Note that this code is pure example, real UnrealScript needs some more work/slightly diffrent syntax)
Code:
class Weapon extents Inventory

class<Projectile> MyProjectile;

function Fire(vector FireLocation, rotator FireDirection)
{
  new MyProjectile(FireLocation, FireDirection);
}
As baseclass for a weapon.
Code:
class Shockrifle extents Weapon

function Init()
{
  MyProjectile = class'Shockball';
}
This allows for all basic firing code to remain in the weapon class, and only the variable needs to be modified if you want to fire a diffrent projectile. The projectile could even be modified by a pickup :)
(Ok, in this example a function "CreateProjectile" that only makes the projectile would also solve it, but read on)

Another use is a random pickup spawner for example, it has an array of class types. And selects a pickup to spawn from that.

Now I'm looking for a way to do this in C++, but I cannot find any. Does it exists at all?

It would clean up this code:
Code:
void CreateRandomItemAt(TVector Location)
{
  switch(rand() % 3)
  {
  case 0: new TPotion(Location); break;
  case 1: new TSword(Location); break;
  case 2: new TShield(Location); break;
  }
}
Into this:
Code:
'class' RandomItems[MAX_ITEMS];

void CreateRandomItemAt(TVector Location)
{
  new RandomItems[rand() % MAX_ITEMS](Location);
}
(Also example code, the real code will include weight factors and stuff, just need to get this sorted first)


(PS, I could be totaly nuts for wanting something like this :blink: )
 
I don't think what your asking for is possible within the normal realms of C++ - the compiler needs to know the exact class name so it knows how much memory to allocate and what constructors to call.

You could possibly fudge it by allocating the memory yourself, but since another part of your program will need to know how to handle it later on, your going to end up with a switch statement to cast that pointer into an appropriate type anyway so you can play with it's methods (unless your only going to use the none-overridden base class methods, in which case, just allocate everything as a base class), so your not going to save much, and may introduce instability by fudging things.
 
I'm not 100% sure what your after, am I correct in saying you want to have a var that changes the type of object that is allocated? I would say that this is always going to involve a switch statement, an array of creation functions (jump table) or a data driven weapon class that is created from an array of data. So your not going to get an easy solution.

I would expect that the unreal stuff runs a kind jump table/data table in the script interpreter, at a guess.

I would just have a switch statement, you may also consider making the base class use a memory pool instead of memory allocation on the fly. (but your prob already doing something like that though) ;)

One company I worked at had a creation function for each class type and a matching unique ID for the class. At boot they would 'register' a class with a bit of code that then allowed what I think your after, but that always struck me as a bit ott. A pain if you made a new class and forgot to register it. But good for handling objects in a script / data file.
 
Right..... it took me a while to understand, but you're trying to use a variable to decide which type of class to instantiate at run-time, yes?

If that's the case, I'd agree with MadDog; use a consistent base interface to allow a common return and then a switch statement to pick the type.
 
Right..... it took me a while to understand, but you're trying to use a variable to decide which type of class to instantiate at run-time, yes?

If that's the case, I'd agree with MadDog; use a consistent base interface to allow a common return and then a switch statement to pick the type.

Its odd, but its not the first time i've had a chat about a class being used as a var. Can't remeber what the last example was, something like

void func_do_something_then_use_this_class_if_the_result_is_true( player->head_class );

Head class being == to say, for argument sake, class CDeadHead

Only can be done with a switch or func pointer. Shame really. In some cases a template may help but that can't be changed at runtime. So the best with the above example would be...

void func_do_something_then_use_this_class_if_the_result_is_true( player::head_class );

with player::head_class typedef'd to class CDeadHead, but this is not dynamic.
 
Last edited by a moderator:
Waaaait, are we not being clear here?

If you want a variable that can point to a instance of a class, but that class coudl change.. your rule is simple..

A pointer points to something; you can point it at anything you like (cast to wrong types to force it, etc), but thats only safe if you have some other mechanism to know what your pointing at.. the normal method of operation is to have a common base class.

ie:

class A
class B
class C

If all 3 classes inhereit from base class Momma, then you could just:

Momma *foo

and later foo could point to A or B or C types, since they're all common base.

You could also use a container..

Class Gamma {
Mamma *poop;
enum MyType type;
}

Then have a pointer to Gamma, which lets get at stuff.. you could put accessor functions on that, so that it acts as an "adaptor".

Then use a Factory to create Gamma..

Gamma *GetRandomWeapon ( ... );

The idea being you use a factory method (a single funciton to create the thing, however it gets created), and use a common 'interface' .. the Gamma would just be a wrapper that does the right thing, or use a common base class like Momma that knows what to do with the various subtypes or in turn calls functions in the right type through virtual inheritence...

But I don't think we're all clear on what you're asking :)

Now, remember, you can also just do it as C code..

typedef struct {
enum MyType type
} base_class;

typedef struct weaponA {
base_class base;
int myweapncrap;
} poop;

Then you can refer to "(base_class*) foo" to get the type, and then refer to "poop" to get the actual goods once you've handed to the right code... (thats all C++ does, more or less)

jeff
 
It would clean up this code:
Code:
void CreateRandomItemAt(TVector Location)
{
  switch(rand() % 3)
  {
  case 0: new TPotion(Location); break;
  case 1: new TSword(Location); break;
  case 2: new TShield(Location); break;
  }
}
Into this:
Code:
'class' RandomItems[MAX_ITEMS];

void CreateRandomItemAt(TVector Location)
{
  new RandomItems[rand() % MAX_ITEMS](Location);
}
(Also example code, the real code will include weight factors and stuff, just need to get this sorted first)

Since each class's constructor has a different type signature, you can't create an array that will hold any of them. But they need to have a common base class anyway in order to be interchangeable, so you can create wrapper functions for them with the same signature and put pointers to *those* in an array:
Code:
// Assuming TPotion, TSword, TShield all derive from TItem...
typedef TItem *MakeItemF(const TVector &);
TItem *mkPotion(const TVector &location) { return new TPotion(location); }
TItem *mkSword(const TVector &location) { return new TSword(location); }
TItem *mkShield(const TVector &location) { return new TShield(location); }

MakeItemF * const RandomItems[] = { &mkPotion, &mkSword, &mkShield };
void CreateRandomItemAt(const TVector &Location)
{
  new RandomItems[rand() % (sizeof(RandomItems)/sizeof(*RandomItems))](Location);
}

And you can use macros/templates to make it less typing to create the mk* functions.
 
Last edited by a moderator:
Right..... it took me a while to understand, but you're trying to use a variable to decide which type of class to instantiate at run-time, yes?
Yes, that's it.

They are all from the same baseclass (TObject in my case). So there isn't a
'simple'/'pure' way to do it. Probly has to do with that constructors can have diffrent parameters.

I like the design from "rabidcow", I'll think I'll go for that. That only needs 1 line added to the code (new mk* function). While the factory way would need a define or enum and an new case.
 
Back
Top