C++ factory problem

Started by
13 comments, last by sepharion 14 years, 1 month ago
Yep, I placed a breakpoint at the top of the function declaration and was stepping into everything. As soon as the program is run the breakpoint turns into the red circle outline with warning symbol. A breakpoint where the function is being called from the other class is reached perfectly fine however.

I don't have access to my code at the minute but the only thing I can think of is that the view is grabbing a local variable instead of the one inside the player class. It's just odd that it chooses once I've got the inheritance tree of game types sorted to stop animating while it worked beforehand (well, in HumanGame, AIGame's animation has never worked due to how it works and that I've not had time). I'll have a look in a few hours when I can access my code
Advertisement
"As soon as the program is run the breakpoint turns into the red circle outline with warning symbol."

That typically means that the code doesn't exist in the target. Which implies a compile-time problem, rather than a runtime one.

Is it possible that you have slightly different method signatures? And that therefore instead of your derived method, the compiler is calling your base class's method?

{And therefore that your derived method is simply never being linked into the exe}
katie - nextWalkFrame isn't an overridden virtual method, the breakpoint is at the base class function definition. Hovering over the icon says something along the lines of "The code executing will never reach this statement"
Stop.

Let's start from the beginning, because there is a lot of confusion in this thread.

This poster is correct, at least this far:

Quote:
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


Look at the expression carefully.

'*game' means "whatever 'game' currently points at". '=' means "should now be assigned". "HumanGame(hInstance, hWnd)" means "an instance of HumanGame".

This is not what you want. For one thing, currently 'game' doesn't point at anything, so "what it points at" can't be assigned anything, because there is nothing to assign to. You must understand here that "assigned" basically means "overwritten with".

For another, if 'game' already points at another type of Game, then assigning a HumanGame to that Game will not change its type to HumanGame. Instead, it will do some kind of conversion. For types that are related by polymorphism, this usually means that the object gets "sliced"; i.e., only the parts of the data specified in the base Game get copied.

What you really want to say is "game should now point at an instance of HumanGame". However, you can't just use a local variable or a temporary, because those won't exist any more after 'createGame' finishes, but the pointer will. To get around this, we can use dynamic allocation.

And you don't really want to assign to a global pointer, either. The purpose of the return value of a function is to return the result of a calculation. The "calculation" performed by a function called "createGame" is to create a Game instance. Therefore, the return value should provide the Game that was created. In other words, have it return the Game*, instead of assigning it somewhere.

Finally, in C++, we use the standard library class std::string to represent text.

Game* createGame(HWND hWnd, HINSTANCE hInstance, const std::string& flags){	RECT screen;	GetClientRect(hWnd, &screen);	if (flags == "-ai") // and this is one of the reasons we use std::string.	{		return new HumanGame(hInstance, hWnd);	}	else	{		return new AIGame(hInstance, hWnd);	}}


If you want to assign to the pointer from somewhere else, you can do so (e.g. 'game = CreateGame(whatever)'). But you should still try to avoid global variables. The natural way to get information to functions is to pass it in the parameters. Scope variables tightly where possible.

Forget about casting for now. It is not relevant to anything you're doing yet.

For the current problem, you need to show more code. In particular, I would like to see the definition for each class.
"Finally, in C++, we use the standard library class std::string to represent text."

The only reason I wasn't doing this was that I was taking the parameter directly from the auto-generated VS code.

//BasicGame.h#ifndef BASICGAME_H#define BASICGAME_H#pragma once#include "Player.h"#include "Map.h"#include "ItemFactory.h"class BasicGame{public:	DWORD startDirTime;	Player player;		Map map;	void nextWalkFrame();	bool collision(char x);	bool onSomething(void);	void createInstances(void);	virtual void updateWorld() = 0;	virtual void sanitise() = 0;	Player getPlayer();	Map getMap();	void runRight();	void runLeft();	void jump();	void walkRight();	void walkLeft();	void doNothing();	void gameChecks();	BasicGame(void);	~BasicGame(void);};#endif//HumanGame.h#ifndef HUMANGAME_H#define HUMANGAME_H#include "Player.h"#include "Keyboard.h"#include "BasicGame.h"#pragma onceclass Game: public BasicGame{private:			char* inputState;	DWORD startDirTime;	Keyboard keyboard;public:	virtual void updateWorld();	void sanitise();	bool nothingPressed(char* state);	HumanGame(HINSTANCE hInstance, HWND hWnd);	HumanGame(void);	~HumanGame(void);};#endif//AIGame.h#ifndef AIGAME_H#define AIGAME_H#pragma once#include <iostream>#include <winsock2.h>#include <Ws2tcpip.h>#include <windows.h>#include "BasicGame.h"class AIGame :	public BasicGame{		public:	void setup(void);	void setupSocket(SOCKET &socket);	void setupWinsock(void);	void bindSocket(SOCKET *socket);	void listenForCon(SOCKET &socket);	void setupClientSocket(SOCKET &socket1, SOCKET &socket2);	void updateWorld(void);	void getX(void);	void getY(void);	void parse(std::string received);	void sanitise(void);	void echo(int x);	AIGame(HINSTANCE hInstance, HWND hWnd);	AIGame(void);	~AIGame(void);};#endif



If it was the actual definitions of the functions you were looking for

//BasicGame.cpp#include "StdAfx.h"#include "BasicGame.h"BasicGame::BasicGame(void){}void BasicGame::createInstances(){	player.setFrameNumber(0);	player.setPos(D3DXVECTOR3(200, 450, 0.0f));	player.setRight(true);	player.setTopPixel(0);	player.setLastUpdate(timeGetTime());	map.Load("level1\0");}bool BasicGame::onSomething(){	if(map.isSolid(player.getPos().x, player.getPos().y))	{		return true;	}	return false;}bool BasicGame::collision(char x){		switch(x)	{	case 'u':	if(player.getRight())				{					if(	map.isSolid(player.getPos().x, player.getPos().y) ||					map.isSolid(player.getPos().x + 10, player.getPos().y))					{return true;}					return false;				}					if(map.isSolid(player.getPos().x - 14, player.getPos().y) ||						map.isSolid(player.getPos().x, player.getPos().y))					{return true;}					return false;	case 'l':	if(	map.isSolid(player.getPos().x - 10, player.getPos().y + 42) ||					map.isSolid(player.getPos().x - 10, player.getPos().y +3)   ||					map.isSolid(player.getPos().x - 10, player.getPos().y +32)   )					{return true;}					return false;	case 'r':	if(	map.isSolid(player.getPos().x + 15, player.getPos().y + 42) ||					map.isSolid(player.getPos().x + 15, player.getPos().y + 3)   ||					map.isSolid(player.getPos().x + 15, player.getPos().y +32)   )					{return true;}					return false;	case 'd':	if(player.getRight())				{				return map.isSolid(player.getPos().x, player.getPos().y + 50);				}				return map.isSolid(player.getPos().x - 5, player.getPos().y + 50);	}	return false;}void BasicGame::nextWalkFrame(){	player.setFrameNumber(player.getFrameNumber() +1);	if(player.getFrameNumber() > 8) // 8 frames of walking animation	{		player.setFrameNumber(0);	}}//fix these getters to use references, this isn't java!//although I don't think they should ever be called.Player BasicGame::getPlayer(){	return player;}Map BasicGame::getMap(){	return map;}void BasicGame::runLeft(){	if(player.getRight())	{		player.setRight(false);		player.setFrameNumber(0);		startDirTime = timeGetTime();	}	else	{		DWORD timeInDir = timeGetTime() - startDirTime;		if(timeInDir > 100)		{			nextWalkFrame();						startDirTime = timeGetTime();		}	}	if(player.getPos().x > 21 && !collision('l')) // offset of the sprite, should really #define this	{		player.runLeft();	}}void BasicGame::runRight(){	if(!player.getRight())	{		player.setRight(true);		player.setFrameNumber(0);		startDirTime = timeGetTime();	}	else	{		DWORD timeInDir = timeGetTime() - startDirTime;		if(timeInDir > 100)		{ 			nextWalkFrame();						startDirTime = timeGetTime();		}	}		if(player.getPos().x < (map.getWidth()*32) - 38) //again offset of the sprite	{				if(!collision('r'))		{			player.runRight();		}	}}void BasicGame::walkLeft(){	if(player.getRight())	{		player.setRight(false);		player.setFrameNumber(0);		startDirTime = timeGetTime();	}	else	{		DWORD timeInDir = timeGetTime() - startDirTime;		if(timeInDir > 100)		{			nextWalkFrame();						startDirTime = timeGetTime();		}	}	if(player.getPos().x > 21 && !collision('l')) // offset of the sprite	{		player.walkLeft();	}}void BasicGame::walkRight(){	if(!player.getRight())	{		player.setRight(true);		player.setFrameNumber(0);		startDirTime = timeGetTime();	}	else	{		DWORD timeInDir = timeGetTime() - startDirTime;		if(timeInDir > 100)		{			nextWalkFrame();								startDirTime = timeGetTime();		}	}	if(player.getPos().x < (map.getWidth() * 32) - 38 && !collision('r'))	{		player.walkRight();	}}void BasicGame::jump(){	if(collision('d'))	{		player.jump();	}}void BasicGame::doNothing(){		if(player.getFrameNumber() != 0)		{			player.setFrameNumber(0);		}}					void BasicGame::gameChecks(){	if(player.getPos().y > map.getHeight()* 32)	{		player.lives--;		player.pos = D3DXVECTOR3(200,450,0.0f);	}	if(collision('u') && player.yAccel < 0)	{		player.stopJump();						map.map[player.pos.y/32][player.pos.x/32].hit();		if(player.getRight())		{			map.map[player.pos.y/32][(player.pos.x +3)/32].hit();		}		else		{			map.map[player.pos.y/32][(player.pos.x -5)/32].hit();		}	}		player.update(collision('d'));	}BasicGame::~BasicGame(void){}

//HumanGame.cpp//although directInput deprecated for mouse/keyboard using it in this case to get to grips with directInput and future expansion should allow gamepads#include "StdAfx.h"#include "HumanGame.h"#define KEYDOWN(name, key) (name[key] & 0x80)char* inputState;DWORD startDirTime;Keyboard keyboard;HumanGame::HumanGame(HINSTANCE hInstance, HWND hWnd){	keyboard = Keyboard(hInstance, hWnd);	createInstances();}void HumanGame::updateWorld(){	DWORD timeNow = timeGetTime();	RECT screen ;			inputState = keyboard.getInput();	if(inputState != NULL)	{		if(KEYDOWN(inputState, DIK_LEFT) && !KEYDOWN(inputState, DIK_LSHIFT))		{			walkLeft();		}		if(KEYDOWN(inputState, DIK_LEFT) && KEYDOWN(inputState, DIK_LSHIFT))		{			runLeft();					}				if(KEYDOWN(inputState, DIK_RIGHT) && !KEYDOWN(inputState, DIK_LSHIFT))		{			nextWalkFrame();			walkRight();		}		if(KEYDOWN(inputState, DIK_RIGHT) && KEYDOWN(inputState, DIK_LSHIFT))		{			runRight();		}		if(KEYDOWN(inputState, DIK_SPACE))		{			jump();		}		if(nothingPressed(inputState))		{			doNothing();		}	}		HumanGameChecks();		}bool HumanGame::nothingPressed(char* keyboardState){	if(		KEYDOWN(keyboardState, DIK_UP)   ||		KEYDOWN(keyboardState, DIK_LEFT) ||		KEYDOWN(keyboardState, DIK_RIGHT)	  )	{		return false;	}	return true;}void HumanGame::sanitise(){	keyboard.cleanup();}HumanGame::HumanGame(){};HumanGame::~HumanGame(void){	sanitise();}

//AIGame.cpp#include "stdafx.h"#include "AIGame.h"#define DEFAULT_PORT "25555"#define DEFAULT_IP "127.0.0.1"#define DEFAULT_BUFLEN 512char recvbuf[DEFAULT_BUFLEN];char sendbuf[DEFAULT_BUFLEN];int iResult, iSendResult;int recvbuflen = DEFAULT_BUFLEN;struct addrinfo *result = NULL, hints;SOCKET clientSock = INVALID_SOCKET;std::string lastCommand;struct timeval timeout;FD_SET read;AIGame::AIGame(HINSTANCE hInstance, HWND hWnd){	createInstances();	setup();}AIGame::~AIGame(void){}void AIGame::getX(){		sprintf(sendbuf, "%d", player.pos.x);	iResult = send(clientSock, sendbuf, recvbuflen, 0);}void AIGame::getY(){	sprintf(sendbuf, "%d", player.pos.y);	iResult = send(clientSock, sendbuf, recvbuflen, 0);}void AIGame::setup(){		SOCKET listenSocket = INVALID_SOCKET;		setupWinsock();	setupSocket(listenSocket);	bindSocket(&listenSocket);	listenForCon(listenSocket);	FD_ZERO(&read);	FD_SET(listenSocket, &read);	MessageBox(NULL, L"Please launch your AI agent program to connect to \n127.0.0.1 on port 25555, click OK after you have done this", L"Warning!", MB_OK | MB_ICONEXCLAMATION);	setupClientSocket(clientSock, listenSocket);	}void AIGame::updateWorld(){		if(select(0,&read,NULL,NULL, &timeout) != 0)		{			iResult = recv(clientSock, recvbuf, recvbuflen, 0);			if (iResult > 0)			{				parse(recvbuf);			}		}		else		{			if(lastCommand.compare("stop") != 0)			{				parse(lastCommand);			}			gameChecks();		}}void AIGame::parse(std::string input){	if(input.compare("jump") == 0)	{		jump();			}	else if(input.compare("run_right") == 0)	{		lastCommand = "run_right";		runRight();			}	else if(input.compare("run_left") == 0)	{		lastCommand = "run_left";			}	else if(input.compare("walk_right") == 0)	{		lastCommand = "walk_right";		walkRight();			}	else if(input.compare("walk_left") == 0)	{		lastCommand = "walk_left";		walkLeft();					}	else if(input.compare("getX") == 0)	{		getX();	}	else if(input.compare("getY") == 0)	{		getY();	}	else if(input.compare("stop") == 0)	{		lastCommand = "stop";		doNothing();			}	else if(input.compare("exit") == 0)	{				iResult = shutdown(clientSock, SD_SEND);		if (iResult == SOCKET_ERROR)		{			closesocket(clientSock);			WSACleanup();		}		closesocket(clientSock);		WSACleanup();	}	gameChecks();}void AIGame::setupWinsock(){	WORD winsockVersion = MAKEWORD(2, 2);	WSAData wsaData;		WSAStartup(winsockVersion, &wsaData);	}void AIGame::setupSocket(SOCKET &listenSocket){	ZeroMemory(&hints, sizeof (hints));	hints.ai_family = AF_INET;	hints.ai_socktype = SOCK_STREAM;	hints.ai_protocol = IPPROTO_TCP;	hints.ai_flags = AI_PASSIVE;	int sockErr = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);	if(sockErr == 0)	{		listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);	}	else	{				WSACleanup();	}	if(listenSocket == INVALID_SOCKET)	{		freeaddrinfo(result);		WSACleanup();			}}void AIGame::bindSocket(SOCKET *listenSocket){	int bindErr = bind( *listenSocket, result->ai_addr, (int)result->ai_addrlen);	if(bindErr == SOCKET_ERROR)	{		freeaddrinfo(result);		closesocket(*listenSocket);		WSACleanup();	}	freeaddrinfo(result);	timeout.tv_sec = 0;	timeout.tv_usec = 3333;	setsockopt(*listenSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));}void AIGame::setupClientSocket(SOCKET &clientSock, SOCKET &listenSocket){	clientSock = accept(listenSocket, NULL, NULL);	if(clientSock == INVALID_SOCKET)	{		WSACleanup();		closesocket(listenSocket);	}	}void AIGame::listenForCon(SOCKET &listenSocket){		int listenErr = listen(listenSocket, SOMAXCONN);	if (listenErr == SOCKET_ERROR)	{		closesocket(listenSocket);		WSACleanup();			}}void AIGame::echo(int x){	send(clientSock, (char*)x,sizeof((char*)x),0);}void AIGame::sanitise(){} //no longer really required



In this case the "slicing" of the BaseGame to form an instance of HumanGame doesn't appear to cause any problems, I used Jansic's idea and am currently using:
if(strcmp(flags, "-ai") != 0){	game = new Game(hInstance, hWnd);}else{	game = new AIGame(hInstance, hWnd);}


I know there is a lot that needs rewritten in this (using getters rather than giving everything direct access being a big part, still getting my head round passing by reference rather than value (damn Java background!))

On the same note as my later posts about the problems with animation - calls of the BasicGame class work up until gameChecks() gets called, at which point player and map are fine, but as soon as the program enters the switch case of collision(char x) everything suddenly shows "CXX0030: Error: expression cannot be evaluated"

Sorry for the long-winded post, and thanks a lot Zahlman, as usual you're being incredibly helpful!

This topic is closed to new replies.

Advertisement