Game Programming and Development Tools

C++ Object Factory help – dartsman

dartsman

Member

Posts: 484
From: Queensland, Australia
Registered: 03-16-2006
Hey, I've been looking into Object Factory creation, and I'm just wondering if it's possible to do something along the lines of...

class Base
{
public:
virtual void init();
void render();
void create();
};

class Obj : public Base
{
public:
void render();
void create();
private:
// extra stuff
};

class objManager
{
public:
Base *newObject()
{
Base *b = new Base();
b->init();
return b;
};
};

...

objManager manager;

Obj *obj = manager.newObject();
obj->create();

So that later on when I have different objects derived from Base I can just at least initiate the objects and do something along the lines of:

class ObjBox : public Base
{
public:
void render();
void create();
private:
// extra stuff
};

ObjBox *obj2 = manager.newObject();
obj2->create();

Please don't worry about spelling mistakes or syntax mistakes. At the moment I'm static casting the Base to the Obj, and sending the new object through a copy constructor in the Obj class which moves down to the Base class copy constructor.

I don't really want to have to register the new objects with the manager either.

------------------
"But it is God who judges: He brings one down, he exalts another." - Psalm 75:7

[This message has been edited by dartsman (edited July 16, 2006).]

[This message has been edited by dartsman (edited July 16, 2006).]

dartsman

Member

Posts: 484
From: Queensland, Australia
Registered: 03-16-2006
ah, actually found out that the static_cast is pretty much the only way to do it, well if I want to not have to register each new class.

------------------
"But it is God who judges: He brings one down, he exalts another." - Psalm 75:7

SSquared

Member

Posts: 654
From: Pacific Northwest
Registered: 03-22-2005
I believe you will have a problem. You can not just call manager.newObject() and place the returned object into a derived class. In this example, you have created ONLY a Base object. You never actually created an instance of an Obj or ObjBox.

By consolidating what you have, you are attempting to do the following:

ObjBox* box = new Base();

You cannot implicitly change from one class to another, even if it is a derived class. You must first create the derived class (the ObjBox).

You will need your newObject method to know which type of Base subclass to create. For example, you can pass in the type of class you want to create by setting up some #DEFINE's if you are using C++. For example, newObject(OBJ_OBJBOX) will create an instance of an ObjBox.

Casting may remove compilation errors, but will not be safe and I do not think it will give you the results you want. If you do a dynamic_cast for the object resturned from newObject, it should give a NULL when you attempt to cast to ObjBox or Obj. This is an indication the derived class was never really created.

Sorry. I am not sure how well this explains things. I've had a tiring weekend/day and am fairly wiped out at the moment.

jestermax

Member

Posts: 1064
From: Ontario, Canada
Registered: 06-21-2006
i agree that you need to return an object of the derived type but if you're going to have different objects being returned then you need to change:
ObjBox* box = new Base();
to
Base* box = manager.newObject();

this is called polymorphism and is fun. now in the Base class, i'm pretty sure you need to make any function that will be overriden virtual.

SSquared

Member

Posts: 654
From: Pacific Northwest
Registered: 03-22-2005
Exactly. But your two examples above did not do that. Your newObject method is ONLY creating an object of 'Base' and can not properly cast to a derived class. Your newObject method must actually create the derived class for this to work.
jestermax

Member

Posts: 1064
From: Ontario, Canada
Registered: 06-21-2006
actually, i would suggest that you make the functions in the Base class purely virtual:
virtual void render() = 0;
virtual void create() = 0;

that way you can't instantiate the base class as it doesn't do anything by itself, its just an abstract class; you have to extend it to give it functionality.

SSquared

Member

Posts: 654
From: Pacific Northwest
Registered: 03-22-2005
Ooops. Sorry Jestermax. I thought you were dartsman. Your avatars, at quick glance, are similar (to me).

I think a Factory pattern usually does make the 'create' call pure virtual. Good suggestion.

BTW, I was not saying to write:

ObjBox* box = new Base();

I was actually pointing out this is illegal, but is essentially what dartsman was trying to do. I agree, polymorphism is fun. Not using polymorphism is completely un-fun.

jestermax

Member

Posts: 1064
From: Ontario, Canada
Registered: 06-21-2006
its ok, i should really change my logo to something that's actually me..... maybe i'll do that now... maybe i can reference an online photo album site or something for my avatar...
HanClinto

Administrator

Posts: 1828
From: Indiana
Registered: 10-11-2004
Jestermax: Or if you don't want to mess with photo posting services (I find them a pain), just e-mail it to me (hanclint at gmail dot com) and I'll throw it up on some webspace for you to use.

--clint

jestermax

Member

Posts: 1064
From: Ontario, Canada
Registered: 06-21-2006
consider it sent
thanks a lot
dartsman

Member

Posts: 484
From: Queensland, Australia
Registered: 03-16-2006
Note: I don't mean to sound rude or anything in this post.

@jestermax: pure virtual functions on render and create cause errors in newObject()... as you cannot create a "new Base();" as it's an abstract class (error C2259 for reference). Hence why I didn't use them. Plus I want the two to be overrided, but possibly later on I might chuck some code in them as well.

@ssquared: ah, I thought in my reply to my post, it sort of cleared it up (that the solution was found)... I know I cannot just use:
Obj *o = manager.newObject();

and yes.. essentially what I was trying to do is..
ObjBox* box = new Base();

but through a copy constructor (as I was mentioned in my first post) I am actually able to do so with:

ObjBox *box(static_cast<ObjBox*>(manager.newObject()));

ObjBox::ObjBox(const ObjBox &obj)
: Base(obj)
{
}

again why I said "do something along the lines of"...

quote:
You will need your newObject method to know which type of Base subclass to create. For example, you can pass in the type of class you want to create by setting up some #DEFINE's if you are using C++. For example, newObject(OBJ_OBJBOX) will create an instance of an ObjBox.

I already said I didn't want to know about registering classes, this is basically the exact same thing (well at least the reason behind why people then go on to create register functions). And although that does sound 'nice' at first, it will mean that I'll need to continually add more constants and keep newObject updated with every new object (which will get messy if say I have 20 objects)... which is also totally what I don't want to do.

I may as well just pass it the derived object like so:
Obj *o;
manager.newObject(o);

and let the newObject function figure out the type and then create it.

The whole aim of this is so that the Base class will initiate some variables, and then the object will recieve the initiated variables so that it can then be created.

avatar wise... thats why I sort of messed with mine in the first place, cause of all the members with that same avatar... thought it was different enough...


Thanks for replying, but again, its been solved through a static_cast and copy constructors.

------------------
"But it is God who judges: He brings one down, he exalts another." - Psalm 75:7

SSquared

Member

Posts: 654
From: Pacific Northwest
Registered: 03-22-2005
quote:
Originally posted by dartsman:
@ssquared: ah, I thought in my reply to my post, it sort of cleared it up
...
but through a copy constructor (as I was mentioned in my first post) I am actually able to do so with:

ObjBox *box(static_cast<ObjBox*>(manager.newObject()));


Ohhhhh...I did not actually understand what you originally wrote. Now I see what you are saying.

Why don't you want to pass in some type of #DEFINE or some other way of knowing what type of class to create? One of the purposes of a Factory pattern is to spit out the base class and the client will never need to worry about the ACTUAL class created. This way, the client needs no knowledge of derived classes. It simply needs to know the interface defined on the abstract base class.

In the above example, the copy constructor becomes inefficient. You are now allocating memory once for the original Base object and then allocating memory again for the ObjBox object. It is more efficient to allocate memory once for ObjBox. In the copy constructor case: What happens to your original Base object you created? Who deletes it? You will need to remember to write extra code in the copy constructor every time you add a new pointer member variable to Base.

quote:
Originally posted by dartsman:
I already said I didn't want to know about registering classes, this is basically the exact same thing (well at least the reason behind why people then go on to create register functions). And although that does sound 'nice' at first, it will mean that I'll need to continually add more constants and keep newObject updated with every new object (which will get messy if say I have 20 objects)... which is also totally what I don't want to do.

By registering, I interpreted that to mean something else. Registering to me means any new object created through the objManager is registered into some type of list. This way, all of the created objects are then managed and maintained by the objManager, as the name implies.

In reply to the above quote, it is much easier to maintain the updates to newObject then remembering to create a copy constructor each time. And having to remember the exact way to call it as well. By leaving all the code in newObject, if Snoopy is a client to your factory, then Snoopy never needs to know about the actual internal class. All Snoopy cares about is for all of the classes to have the same interface as the one base class. Snoopy just deals with a Base object and doesn't really care what is happening underneath.

quote:
Originally posted by dartsman:
The whole aim of this is so that the Base class will initiate some variables, and then the object will recieve the initiated variables so that it can then be created.

Can you add something like Base::init() as the first line of all the derived classes constructors?

Sorry, all you wanted to know was if what you had will work. I may have taken this off your original intent of the post.

dartsman

Member

Posts: 484
From: Queensland, Australia
Registered: 03-16-2006
quote:
Why don't you want to pass in some type of #DEFINE or some other way of knowing what type of class to create? One of the purposes of a Factory pattern is to spit out the base class and the client will never need to worry about the ACTUAL class created. This way, the client needs no knowledge of derived classes. It simply needs to know the interface defined on the abstract base class.

best answered with what I said earlier:

"I already said I didn't want to know about registering classes, this is basically the exact same thing (well at least the reason behind why people then go on to create register functions)."

and also

"And although that does sound 'nice' at first, it will mean that I'll need to continually add more constants and keep newObject updated with every new object (which will get messy if say I have 20 objects)..."

The #define way actually caused people to use the registered way as it stops them from having to constantly keep updating with a define and in the creation function aswell (aka, a bloated switch statement).

quote:
By registering, I interpreted that to mean something else. Registering to me means any new object created through the objManager is registered into some type of list.

Did I say it wasn't? Yes, that is what registering is, but again, not something I want to do.

You might have got the wrong interpretation of what I said in:

"I already said I didn't want to know about registering classes, this is basically the exact same thing".

'this' was refering (quoted) to the #define which you mentioned I should do.

Again the whole reasoning behind registering classes is because people used to just do the define way. Which was sort of ok, until you find out that your creating a lot of derived classes. The define way makes it a constant process to update the "newObject()" function each time a new object is derived from Base.

The whole point is that the Base has it's init() function, which sets up some variables which every derived class will use. Some of those variables are pointers to certain things which the derived class will need to actually create the object (such as vertices/indices).

I want the manager to do the init() function and setup those variables. So that it will return the setup base object to the class. But each object itself will have different create functions and variables.

quote:
copy constructor becomes inefficient

You are very much right there, it is creating it twice, although it will be deleted twice as well. Though the original Base class is new'd the copy isn't, only the data inside the copy, which will be deleted anyway in the class deconstructor.

quote:
You will need to remember to write extra code in the copy constructor every time you add a new pointer member variable to Base.

Base won't be changing as much as the derived objects will, so changing the copy constructor in Base won't be much of an issue, plus it won't be a major re-factoring of the class each time, so again, not much of an issue.

quote:
Can you add something like Base::init() as the first line of all the derived classes constructors?

No, cause then I would have to have a class constructor which has all of the init parameters to pass through to the init() function. I didn't want to disclose the parameters to public.

Finally, no, I didn't want to know if that code would just work. I was wondering how to get something along the lines of that working. Also without having to register the classes. The code itself was just some mock-up code done rather quickly in Notepad, again, I mentioned not to "Please don't worry about spelling mistakes or syntax mistakes." as I knew there could quiet possibly be some.

I will look into the copy constructor and possibly redo the factory so that I am not new'ing the Base variables twice.

Thanks again for your help.

------------------
"But it is God who judges: He brings one down, he exalts another." - Psalm 75:7

dartsman

Member

Posts: 484
From: Queensland, Australia
Registered: 03-16-2006
Ah-ha!!

figured it out...

it now looks something like:

Obj *o = new Obj();
manager.newObject(o);
o->create();

newObject function looks like:

bool objManager::newObject(Base *obj)
{
if (obj == NULL)
return 0;

obj->init();

return 1;
}

(code could be wrong, but you should get the gist of it)

------------------
"But it is God who judges: He brings one down, he exalts another." - Psalm 75:7

steveth45

Member

Posts: 536
From: Eugene, OR, USA
Registered: 08-10-2005
Dartsman,
The code in your last post will work, unlike the static casting thingy. However, it's hardly an object factory, which is what I thought you were trying to make. The point of an object factory is to not hard-code the creation of specific derived objects in the main code. Gamedev.net has a very basic article on the topic that should clear up any confusion about what an object factory is : http://www.gamedev.net/reference/articles/article2097.asp .

------------------
+---------+
|steveth45|
+---------+

dartsman

Member

Posts: 484
From: Queensland, Australia
Registered: 03-16-2006
true that...

but it just ended up working out easier this way. I have seen that article many times in the past, but find it annoying to have to keep updating the object factory/manager class when a new object is created. So I've created more of a Object Manager rather then an Object Factory.

Also, a side note... "unlike the static casting thingy"?? the static_cast method did actually work.

Anywho, sorry for any mis-understandings... it works, does exactly what it needs to do, case close :P

------------------
"But it is God who judges: He brings one down, he exalts another." - Psalm 75:7

steveth45

Member

Posts: 536
From: Eugene, OR, USA
Registered: 08-10-2005
With static_cast, you can cast from a base class pointer to a derived class pointer. However, if the class was instantiated as the base class (as in your example) the static_cast doesn't fill in any extra data for you that may exist in the derived class. When the copy constructor tries to copy over data that only exists in the derived class, your code will choke. Don't believe me? Read this: http://msdn2.microsoft.com/en-us/library/c36yw7x9.aspx .

------------------
+---------+
|steveth45|
+---------+

dartsman

Member

Posts: 484
From: Queensland, Australia
Registered: 03-16-2006
lol of course...

see the Base class has some variables in it which I wanted initiated through the object manager. I'm not creating the object itself in the object manager, just initiating its Base variables...

In the static_cast ones I just didn't put the "->create();" after, just mainly cause I was lazy, but of course that would be called right after the static_cast. Like so:

ObjBox *box(static_cast<ObjBox*>(manager.newObject()));
box->create();

And with the copy constructor it would do:

ObjBox::ObjBox(const &ObjBox obj)
: Base(obj)
{
// whatever ObjBox needs to init
}

But anywho, I'm not doing that anymore, I've got the thing running, it works and is easy to use (as set out in the first place)...

------------------
"But it is God who judges: He brings one down, he exalts another." - Psalm 75:7