Linker Issues

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
ajtgarber
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 97
Joined: Wed Jun 10, 2009 8:56 am

Linker Issues

Post 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?
Scoody
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 65
Joined: Fri Feb 06, 2009 2:07 pm

Re: Linker Issues

Post 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.
ajtgarber
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 97
Joined: Wed Jun 10, 2009 8:56 am

Re: Linker Issues

Post 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 :)
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: Linker Issues

Post by Falco Girgis »

You should really use initializer lists for variable initialization within your constructor.
wearymemory
Chaos Rift Junior
Chaos Rift Junior
Posts: 209
Joined: Thu Feb 12, 2009 8:46 pm

Re: Linker Issues

Post 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.
User avatar
Ginto8
ES Beta Backer
ES Beta Backer
Posts: 1064
Joined: Tue Jan 06, 2009 4:12 pm
Programming Language of Choice: C/C++, Java

Re: Linker Issues

Post 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. ;)
Quit procrastinating and make something awesome.
Ducky wrote:Give a man some wood, he'll be warm for the night. Put him on fire and he'll be warm for the rest of his life.
Post Reply