Jump to content
  • Advertisement
Sign in to follow this  
sepharion

C++ factory problem

This topic is 3042 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've got an abstract base class BasicGame which has 2 subclasses AIGame and HumanGame, the type of game used is dependant on commandline arguments at runtime so the main executable has a BasicGame declared as a variable. Obviously I can't instantiate an abstract class so I've got a pointer to the base class. My question is this, how the heck do I convert from the abstract base class pointer into an instance of the subtype (presumably will also have to be a pointer)? I know I could declare an instance of both types, instantiate only 1 of them and only call the methods on the instantiated one but that's clearly a horrible hack.
BasicGame *game;

void createGame(HWND hWnd, HINSTANCE hInstance, LPSTR flags)
{
	RECT screen;
	GetClientRect(hWnd, &screen);
	if(strcmp(flags, "-ai") != 0)
	{
		*game = HumanGame(hInstance, hWnd);
	}
	else
	{
		*game = AIGame(hInstance, hWnd);
	}
}

when I run that I get an error in __vfptr and in all members of the subtype class: CXX0030: Error: expression cannot be evaluated. Cheers, Sepharion

Share this post


Link to post
Share on other sites
Advertisement
not sure I totally understand your question, but couldn't you just do a cast on your game pointer to either HumanGame or AIGame?

Share this post


Link to post
Share on other sites
The cause of the issues is that you are not actually allocating the game object in question. Once you do that you can just use the interface to invoke whichever actions need to be done.

For example:
<code>
game = new HumanGame(hInstance, hWnd);
</code>

Share this post


Link to post
Share on other sites
oluf - the game is generally set up the same regardless of which type of game so I'm using the base class to do that, later when I read the commandline input it should determine whether it is a HumanGame or an AIGame to add the specific implementation for a couple of methods.

dysfictional - Can it not be done keeping the game on the stack or do I actually need to give it heap space?

I like oluf's idea of casting the BasicGame pointer to a HumanGame or AIGame pointer but I've never really used static casts before and I'm pretty unsure how I would do it on an abstract class.

The example from cplusplus.com

class CBase {};
class CDerived: public CBase {};
CBase * a = new CBase;
CDerived * b = static_cast<CDerived*>(a);




In this case CBase would be BasicGame however because it is abstract it cannot be instantiated.

Share this post


Link to post
Share on other sites
I think you're confused (and the 'cplusplus.com' example code is also confused). Your original code:

BasicGame *game;
*game = HumanGame(hInstance, hWnd);
Takes whatever HumanGame() spits out and does a by-value copy of it into storage that hasn't been allocated yet; most likely slicing it in the process.
To do what you want you can either allocate dynamically (using new), or take pointers to existing (statically allocated) objects. Either of the following functions will give you the pointer you want:-

BasicGame *HumanGame()
{
static HumanGameObject obj;
return &obj;
}

BasicGame *HumanGame()
{
return new HumanGameObject(); // preferred
}

BasicGame *game = HumanGame();



Jans.

Share this post


Link to post
Share on other sites
Quote:
Original post by sepharion
...
I like oluf's idea of casting the BasicGame pointer to a HumanGame or AIGame pointer but I've never really used static casts before and I'm pretty unsure how I would do it on an abstract class.
...


And why would you want to cast BasicGame pointer to HumanGame or AIGame? Can you show some code about it, since it may be unnecessary.

Regards!


Share this post


Link to post
Share on other sites
use game = new HumanGame(hInstance, hWnd); not *game = HumanGame(hInstance, hWnd);

The example you have shown (from cplusplus.com):

The Base class is not abstract. Classes are abstract there is AT LEAST one pure virtual function inside. And if you want to create a derived class instance do it like that:


Base * child = new Derived;



There is no need of static_cast here. AFAIK static casts are for objects whose types are COMPLETELY different from each other like:

class Base1{};
class Base2{};

Base1 * base1 = new Base1;
Base2 * base2 = static_cast<Base2 *>(base1);


So when you deal with Derived-Base relation use implicit casting, not explicit castings (dynamic_cast, static_cast, reinterpret_cast)

Share this post


Link to post
Share on other sites
kauna - because updateWorld in HumanGame differs drastically from the functionality in AIGame so is a pure virtual function in BasicGame.

kasya - I meant in my case the base class is abstract because updateWorld() is a pure virtual function. Having read up on casts a dynamic cast will cast from subtype to base type only, static cast can do it either way, reinterpret casts can change it from any 1 thing to any other but is likely to cause crashes at runtime.

jans - Roughly what i've got now which seems to work but has broken other things.


void createInstances(HWND hWnd, HINSTANCE hInstance, LPSTR flags)
{

RECT screen;
GetClientRect(hWnd, &screen);
if(strcmp(flags, "-ai") != 0)
{
game = new Game(hInstance, hWnd);
}
else
{
game = new AIGame(hInstance, hWnd);
}

}



seems to be doing the trick, although something that I've noticed now is that my animation has broken (and I think it has something to do with this). Every time the control loop inside my updateWorld() method is called it skips a method call. For simplicities sake if the player holds the right arrow key walkRight() is called inside the base class. walkRight() does checks to make sure the player isn't against a wall and should handle which frame of animation is displayed, however something is preventing this method from being called.



void BasicGame::walkRight()
{
if(!player.goingRight())
{
player.setGoingRight(true);
player.setFrameNumber(0);
startDirTime = timeGetTime(); // startDirTime defined as a DWORD
}
else
{
DWORD timeInDir = timeGetTime() - startDirTime;
if(timeInDir > 100)
{
nextWalkFrame(); //debugger gets here but never enters method

startDirTime = timeGetTime();
}
}

if(!collision('l'))
{
player.walkLeft();

}
}

void BasicGame::nextWalkFrame()
{
player.setFrameNumber(player.getFrameNumber() +1);

if(player.getFrameNumber() > 8) // 8 frames of walking animation
{

player.setFrameNumber(0);
}

}




Any idea why this is being optimised out or is there a way to turn optimisation off?

Cheers,
Sepharion

Share this post


Link to post
Share on other sites
Quote:
Original post by sepharion
because updateWorld in HumanGame differs drastically from the functionality in AIGame so is a pure virtual function in BasicGame.




class BasicGame
{
virtual void UpdateWorld() = 0;
}

void AIGame::UpdateWorld()
{
//implement the AIGame version of update world
}

void HumanGame::UpdateWorld()
{
//implement the HumanGame version of update world
}

//inside your game loop

game->UpdateWorld(); <- this will call the correct updateworld.






Well you still didn't present the case which absolutely requires you to dig up the original type of the BasicGame class. Inheritance is good with dealing such situations where the class implementations differ from each others.

Best regards!

Share this post


Link to post
Share on other sites
Quote:
Any idea why this is being optimised out or is there a way to turn optimisation off?


I am sure a compiler will NEVER optimize a program by omitting a function call that actually does something, and certainly not in debug mode. Are you sure you are using the debugger right? Are you stepping INTO the function (F11 in the VS Debugger) and not just over it?

Have you tried setting a breakpoint within nextWalkFrame() ?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!