Page 1 of 1

Linker Issues

Posted: Fri Aug 06, 2010 11:49 am
by ajtgarber
"FBEngine::instance", referenced from:
FBEngine::getInstance() in FBModel.o
FBEngine::getInstance() in FBModel.o
FBEngine::getInstance() in FBModel.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Code: Select all

//FBEngine.h
#pragma once

#pragma GCC visibility push(default)

#include <ctime>
#include <cstdlib>
#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include "Game.h"

using namespace std;

class FBEngine {
	protected:
		FBEngine();

	private:
		Game* game;
		static FBEngine *instance;
		
	public:
		static inline FBEngine* getInstance() {
			if(instance == NULL) {
				instance = new FBEngine();
			}
			return instance;
		}
		~FBEngine();
		
		Game* getGame();
		void setGame(Game* game);
		
		void start();
};

#pragma GCC visibility pop

Code: Select all

//FBModel.h
#pragma once

#pragma GCC visibility push(default)

#include "FBTexture.h"
#include "Game.h"
#include "FBEngine.h"
//#include "FBBoundingBox.h"
#include <SDL/SDL_opengl.h>

//handles a 3D OpenGL model
class FBModel {
	private:
		//vertex data - stride = 3
		GLfloat* vertices;
		GLuint vertices_length;
		
		//color info
		GLuint* colors;
		GLuint colors_stride;
		GLuint colors_length;

		//texture data
		FBTexture texture;
		GLfloat* textureCoords;
		GLuint textureCoords_length;
		bool usesVerticesAsTextureCoords;
		bool useTexture;

		//normal data - stride = 3
		GLfloat* normals;
		GLuint	normals_length;
		bool containsNormals;

		//Bounding Box
		//FBBoundingBox bounds;

	public:
		FBModel(); //sets everything to 0 or NULL, use setter methods to make this object useful

		virtual ~FBModel();
		
		GLfloat* getVertices();
		GLuint getVerticesLength();
		
		GLuint* getColors();
		GLuint getColorStride();
		GLuint getColorsLength();

		FBTexture getTexture();
		GLfloat* getTextureCoordinates();
		GLuint getTextureCoordinatesLength();
		bool usesVerticesAsTextureCoordinates();
		bool usesTexture();

		GLfloat* getNormals();
		GLuint getNormalsLength();
		bool hasNormals();

		//FBBoundingBox getBounds();

		void setVertices(GLfloat* vertices, GLuint length);
		
		void setColors(GLuint* colors, GLuint colors_stride, GLuint colors_length);
		
		void setTexture(FBTexture texture, GLfloat* textureCoordinates, GLuint textureCoordinates_length);
		void setTexture(FBTexture texture);

		void setNormals(GLfloat* normals, GLuint normals_length);
		
		//void setBounds(FBBoundingBox bounds);

		//renders this model to the screen if OpenGL is enabled
		virtual void render(GLenum mode);
};

#pragma GCC visibility pop

Code: Select all

//FBEngine.cpp
#include "FBEngine.h"

FBEngine::FBEngine() {
	game = 0;
}
FBEngine::~FBEngine() {
	
}

Game* FBEngine::getGame() {
	return game;
}
void FBEngine::setGame(Game* game) {
	this->game = game;
}

void FBEngine::start() {
	cout << "FBEngine: Initializing Game..." << endl;
	game->init();
	cout << "FBEngine: Game Initialized!" << endl;
	cout << "FBEngine: Starting game at " << game->getRequestedFrameRate() << "fps..." << endl;
	
	bool running = true;
	while(running) {
		//listen to events
		SDL_Event event;
		while(SDL_PollEvent(&event)) {
			if(event.type == SDL_QUIT) {
				running = false;
				break;
			} else if(event.type == SDL_KEYUP) {
				game->keyEvent(event.key.keysym.sym, FBE_KEY_RELEASED);
			} else if(event.type == SDL_KEYDOWN) {
				game->keyEvent(event.key.keysym.sym, FBE_KEY_PRESSED);
			} else if(event.type == SDL_MOUSEMOTION ) {
				int x = event.motion.x;
				int y = event.motion.y;
				game->mouseEvent(-1, x, y, FBE_MOUSE_MOVED);
			} else if(event.type == SDL_MOUSEBUTTONDOWN) {
				int x = event.motion.x;
				int y = event.motion.y;
				int button = -1;
				if(event.button.button == SDL_BUTTON_RIGHT) {
					button = FBE_MOUSE_BUTTON_RIGHT;
				} else if(event.button.button == SDL_BUTTON_LEFT) {
					button = FBE_MOUSE_BUTTON_LEFT;
				} else if(event.button.button == SDL_BUTTON_MIDDLE) {
					button = FBE_MOUSE_BUTTON_MIDDLE;
				}
				game->mouseEvent(button, x, y, FBE_MOUSE_PRESSED);
			} else if(event.type == SDL_MOUSEBUTTONUP) {
				int x = event.motion.x;
				int y = event.motion.y;
				int button = -1;
				if(event.button.button == SDL_BUTTON_RIGHT) {
					button = FBE_MOUSE_BUTTON_RIGHT;
				} else if(event.button.button == SDL_BUTTON_LEFT) {
					button = FBE_MOUSE_BUTTON_LEFT;
				} else if(event.button.button == SDL_BUTTON_MIDDLE) {
					button = FBE_MOUSE_BUTTON_MIDDLE;
				}
				game->mouseEvent(button, x, y, FBE_MOUSE_RELEASED);
			}
		}
		if(!running) break; //if we have quit then break out of the loop
		
		//handle a steady frame rate
		int previous = time(0);
		game->update();
		game->render();
		if(game->isUsingOpenGL()) {
			glFlush();
			SDL_GL_SwapBuffers();
		} else {
			SDL_Flip(game->getBuffer());
		}
		int now = time(0);
		int usedTime = now-previous;
		if(usedTime < 1000/game->getRequestedFrameRate()) { //if we didn't take up all the time we had
			SDL_Delay((1000/game->getRequestedFrameRate())-usedTime);
		} else { //if we took too long
			SDL_Delay(5); //wait a small amount anyway
		}
		
		int reportedFrameRate = 1000/usedTime;
		game->setReportedFrameRate(reportedFrameRate);
	}
	cout << "FBEngine: Game Exiting..." << endl;
	game->deinit();
	game->Game::~Game(); //explicitly call Game's destructor to clean up memory (and quit SDL)
}

Code: Select all

//FBModel.cpp
#include "FBModel.h"

FBModel::FBModel() {
		vertices = 0;
		vertices_length = 0;
		
		colors = 0;
		colors_stride = 0;
		colors_length = 0;
		
		textureCoords = 0;
		textureCoords_length = 0;
		useTexture = false;
		usesVerticesAsTextureCoords = false;
		
		normals = 0;
		normals_length = 0;
		containsNormals = false;
}
FBModel::~FBModel() {

}

GLfloat* FBModel::getVertices() {
	return vertices;
}
GLuint FBModel::getVerticesLength() {
	return vertices_length;
}

GLuint* FBModel::getColors() {
	return colors;
}
GLuint FBModel::getColorStride() {
	return colors_stride;
}
GLuint FBModel::getColorsLength() {
	return colors_length;
}

FBTexture FBModel::getTexture() {
	return texture;
}
GLfloat* FBModel::getTextureCoordinates() {
	return textureCoords;
}
GLuint FBModel::getTextureCoordinatesLength() {
	return textureCoords_length;
}
bool FBModel::usesVerticesAsTextureCoordinates() {
	return usesVerticesAsTextureCoords;
}
bool FBModel::usesTexture() {
	return useTexture;
}

GLfloat* FBModel::getNormals() {
	return normals;
}
GLuint FBModel::getNormalsLength() {
	return normals_length;
}
bool FBModel::hasNormals() {
	return containsNormals;
}

/*FBBoundingBox FBModel::getBounds() {
	return bounds;
}*/

void FBModel::setVertices(GLfloat* vertices, GLuint vertices_length) {
	this->vertices = vertices;
	this->vertices_length = vertices_length;
}

void FBModel::setColors(GLuint* colors, GLuint colors_stride, GLuint colors_length) {
	this->colors = colors;
	this->colors_stride = colors_stride;
	this->colors_length = colors_length;
}

void FBModel::setTexture(FBTexture texture, GLfloat* textureCoords, GLuint textureCoords_length) {
	this->texture = texture;
	this->textureCoords = textureCoords;
	this->textureCoords_length = textureCoords_length;
	this->useTexture = true;
	this->usesVerticesAsTextureCoords = false;
}
void FBModel::setTexture(FBTexture texture) {
	this->texture = texture;
	this->textureCoords = 0;
	this->textureCoords_length = 0;
	this->useTexture = true;
	this->usesVerticesAsTextureCoords = true;
}

void FBModel::setNormals(GLfloat* normals, GLuint normals_length) {
	this->normals = normals;
	this->normals_length = normals_length;
	this->containsNormals = true;
}

/*void FBModel::setBounds(FBBoundingBox bounds) {
	this->bounds = bounds;
}*/

void FBModel::render(GLenum mode) {
	FBEngine* engine = FBEngine::getInstance();
	if(engine->getGame()->isUsingOpenGL()) {
		if(textureCoords_length != 0) {
			glEnableClientState(GL_VERTEX_ARRAY);
			glEnableClientState(GL_COLOR_ARRAY);
			glEnableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			
			glVertexPointer(3, GL_FLOAT, 0, vertices);
			if(colors_length != 0)
				glColorPointer(colors_stride, GL_UNSIGNED_INT, 0, colors);
			if(usesVerticesAsTextureCoords)
				glTexCoordPointer(2, GL_FLOAT, 0, vertices);
			else
				glTexCoordPointer(2, GL_FLOAT, 0, textureCoords);
			if(containsNormals)
				glNormalPointer(GL_FLOAT, 0, normals);
			
			glDrawArrays(mode, 0, vertices_length);
			
			glDisableClientState(GL_VERTEX_ARRAY);
			glDisableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		}
	}
}

uhmm...okay... what do I do? FBModel includes the FBEngine's header file, how can't it find this? or am I just interpreting this incorrectly?

As a little side question, anyone else get really pissed at their compiler/linker every once in a while?

Re: Linker Issues

Posted: Fri Aug 06, 2010 1:08 pm
by Scoody
Include the header file in FBModel.cpp and use "class FBEngine;" in FBModel.h instead of including it. This is good enough as long as you're using pointers since the interpreter doesn't need to know anything about the class at that point other than that it's pointing to a class.

Re: Linker Issues

Posted: Fri Aug 06, 2010 1:47 pm
by ajtgarber
I figured out what it was :P, it couldn't see the FBEngine* instance; I'm assuming this is because it was an inline function, but I changed it to:

Code: Select all

static inline FBEngine* getInstance() {
        static FBEngine* instance;
        if(instance == NULL)
              instance = new FBEngine();
        return instance;
}
I'm probably going to get rid of the inline though

Thanks for your help :)

Re: Linker Issues

Posted: Fri Aug 06, 2010 4:42 pm
by Falco Girgis
You should really use initializer lists for variable initialization within your constructor.

Re: Linker Issues

Posted: Fri Aug 06, 2010 9:48 pm
by wearymemory
Lazy initialization can actually harm performance if used in situations where it can be avoided, but it also has its purposes. Is your FBEngine really that costly to initialize? If not, or if you are sure that an instance of your FBEngine will always be created, then it may be more preferable to initialize it in your class. This could prevent race conditions if you were to implement threading.

Re: Linker Issues

Posted: Tue Aug 10, 2010 10:11 pm
by Ginto8
GyroVorbis wrote:You should really use initializer lists for variable initialization within your constructor.
agreed. Though it can sometimes cause a bit of code clutter in constructors with a lot of options, it is definitely worth not having to copy-paste code and also having flexibility when initializing.
wearymemory wrote:Lazy initialization can actually harm performance if used in situations where it can be avoided, but it also has its purposes. Is your FBEngine really that costly to initialize? If not, or if you are sure that an instance of your FBEngine will always be created, then it may be more preferable to initialize it in your class. This could prevent race conditions if you were to implement threading.
This is a pretty good point. What you should probably be doing is having an Init() function somewhere that creates the FBEngine. Have GetInstance() return a pointer, so that you can return NULL sometimes. This will require a little more intelligence on the end of the engine's user, but since the user is also the person who's writing it (aka you), that shouldn't be an issue. I hope. ;)