Game Development Community

Inclusion Guards

by Andy "Code" Cook · in Technical Issues · 06/14/2002 (3:25 pm) · 8 replies

I keep having this problem where the compiler is giving me linker errors saying that all of the functions and variables in this particular header fileof mine has already been define in main.obj.(11 errors total) I thought this was because I use this header file multiple times. So I just put in an Inclusion gaurd that looks like this.

#ifndef TEXTURE_H
#define TEXTURE_H

//All of my header file went here

#endif

but it's not working. I understand that it's hard to answer this question without the whole project but it's kinda big and sloppy. (Mostly just sloppy code I am too embarrasb to show)

If it helps I am using Microsoft Visual C++ 6


Heres what the Debuger says:


Compiling...
main.cpp
Skipping... (no relevant changes detected)
Piece.cpp
Linking...
Piece.obj : error LNK2005: "struct _AUX_RGBImageRec * __cdecl LoadBMP(char *)" (?LoadBMP@@YAPAU_AUX_RGBImageRec@@PAD@Z) already defined in main.obj
Piece.obj : error LNK2005: "int __cdecl LoadGLTextures(void)" (?LoadGLTextures@@YAHXZ) already defined in main.obj
Piece.obj : error LNK2005: "void __cdecl BuildFont(void)" (?BuildFont@@YAXXZ) already defined in main.obj
Piece.obj : error LNK2005: "void __cdecl KillFont(void)" (?KillFont@@YAXXZ) already defined in main.obj
Piece.obj : error LNK2005: "void __cdecl glPrint(int,int,char *,int)" (?glPrint@@YAXHHPADH@Z) already defined in main.obj
Piece.obj : error LNK2005: "unsigned int * texture" (?texture@@3PAIA) already defined in main.obj
Piece.obj : error LNK2005: "float cnt2" (?cnt2@@3MA) already defined in main.obj
Piece.obj : error LNK2005: "float cnt1" (?cnt1@@3MA) already defined in main.obj
Piece.obj : error LNK2005: "unsigned int base" (?base@@3IA) already defined in main.obj
Piece.obj : error LNK2005: "unsigned int loop" (?loop@@3IA) already defined in main.obj
Debug/Multiplayer Pong.exe : fatal error LNK1169: one or more multiply defined symbols found
Error executing link.exe.

#1
06/14/2002 (3:30 pm)
Usually when you do inclusion guards in the header files you do it over everything and not just the includes. If you do it in just the includes then its still compiling the rest of the header.

The best would be to put inclusion guards on all your headers that way they are only linked into the project one time.
#2
06/14/2002 (3:35 pm)
I did put the gaurd over the whole header file with all the header code in the if statement. (It's really weird it's not working) Ill show you the header file:

#ifndef TEXTURE_H
#define TEXTURE_H

#include
#include // Header File For Windows Math Library
#include // Header File For Windows
#include
#include
#include

GLuint base; // Base Display List For The Font
GLuint texture[2]; // Storage For Our Font Texture
GLuint loop; // Generic Loop Variable

GLfloat cnt1; // 1st Counter Used To Move Text & For Coloring
GLfloat cnt2; // 2nd Counter Used To Move Text & For Coloring


////////////////////////////////////////////////
//Definitions For Texture.cpp Functions
////////////////////////////////////////////////




AUX_RGBImageRec *LoadBMP(char *Filename);

int LoadGLTextures();

GLvoid BuildFont(GLvoid);

GLvoid KillFont(GLvoid) ;

GLvoid glPrint(GLint x, GLint y, char *string, int set);



///////////////////////////////////////////////////////
//////BMP READER
///////////////////////////////////////////////////////
AUX_RGBImageRec *LoadBMP(char *Filename) // Loads A Bitmap Image
{
FILE *File=NULL; // File Handle
if (!Filename) // Make Sure A Filename Was Given
{
return NULL; // If Not Return NULL
}
File=fopen(Filename,"r"); // Check To See If The File Exists
if (File) // Does The File Exist?
{
fclose(File); // Close The Handle
return auxDIBImageLoad(Filename); // Load The Bitmap And Return A Pointer
}
return NULL; // If Load Failed Return NULL
}




///////////////////////////////////////////////////////
//////TEXTURE LOADER
///////////////////////////////////////////////////////
int LoadGLTextures() // Load Bitmaps And Convert To Textures
{
int Status=FALSE; // Status Indicator
AUX_RGBImageRec *TextureImage[2]; // Create Storage Space For The Textures

memset(TextureImage,0,sizeof(void *)*2); // Set The Pointer To NULL

if ((TextureImage[0]=LoadBMP("Fire.bmp")) && // Load The Font Bitmap
(TextureImage[1]=LoadBMP("Bump.bmp"))) // Load The Texture Bitmap
{
Status=TRUE; // Set The Status To TRUE

glGenTextures(2, &texture[0]); // Create Two Texture

for (loop=0; loop<2; loop++) // Loop Through All The Textures
{
// Build All The Textures
glBindTexture(GL_TEXTURE_2D, texture[loop]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
}
}
for (loop=0; loop<2; loop++)
{
if (TextureImage[loop]) // If Texture Exists
{
if (TextureImage[loop]->data) // If Texture Image Exists
{
free(TextureImage[loop]->data); // Free The Texture Image Memory
}
free(TextureImage[loop]); // Free The Image Structure
}
}
return Status; // Return The Status
}




///////////////////////////////////////////////////////
//////FONT BUILDER
///////////////////////////////////////////////////////
GLvoid BuildFont(GLvoid) // Build Our Font Display List
{
float cx; // Holds Our X Character Coord
float cy; // Holds Our Y Character Coord

base=glGenLists(256); // Creating 256 Display Lists

glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Font Texture
for (loop=0; loop<256; loop++) // Loop Through All 256 Lists
{
cx=float(loop%16)/16.0f; // X Position Of Current Character
cy=float(loop/16)/16.0f; // Y Position Of Current Character

glNewList(base+loop,GL_COMPILE); // Start Building A List


glBegin(GL_QUADS); // Use A Quad For Each Character
glTexCoord2f(cx,1-cy-0.0625f); // Texture Coord (Bottom Left)
glVertex2i(0,0);

glTexCoord2f(cx+0.0625f,1-cy-0.0625f); // Texture Coord (Bottom Right)
glVertex2i(16,0); // Vertex Coord (Bottom Right)

glTexCoord2f(cx+0.0625f,1-cy); // Texture Coord (Top Right)
glVertex2i(16,16);

glTexCoord2f(cx,1-cy); // Texture Coord (Top Left)
glVertex2i(0,16); // Vertex Coord (Top Left)
glEnd();

glTranslated(10,0,0); // Move To The Right Of The Character
glEndList(); // Done Building The Display List
} // Loop Until All 256 Are Built
}




///////////////////////////////////////////////////////
//////KILL FONT
///////////////////////////////////////////////////////
GLvoid KillFont(GLvoid) // Delete The Font From Memory
{
glDeleteLists(base,256); // Delete All 256 Display Lists
}



///////////////////////////////////////////////////////
//////PRINT FONT
///////////////////////////////////////////////////////
GLvoid glPrint(GLint x, GLint y, char *string, int set) // Where The Printing Happens
{
if (set>1) // Is set Greater Than One?
{
set=1; // If So, Make Set Equal One
}

glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Font Texture
glDisable(GL_DEPTH_TEST); // Disables Depth Testing
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glPushMatrix();
glLoadIdentity(); // Reset The Projection Matrix
glOrtho(0,640,0,480,-1,1);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glPushMatrix(); // Store The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix

glTranslated(x,y,0); // Position The Text (0,0 - Bottom Left)
glListBase(base-32+(128*set)); // Choose The Font Set (0 or 1)
glCallLists(strlen(string),GL_BYTE,string); // Write The Text To The Screen

glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glPopMatrix(); // Restore The Old Projection Matrix

glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glPopMatrix(); // Restore The Old Projection Matrix

glEnable(GL_DEPTH_TEST); // Enables Depth Testing
}

#endif
#3
06/14/2002 (3:52 pm)
Prototypes go in the header, the body of the functions should be in a .cpp file.
#4
06/14/2002 (4:08 pm)
Ya I just through those in so you saw the functions, but I am not sure if that matters does it? well any way here is the actual pure Texture.h file:


#ifndef TEXTURE_H
#define TEXTURE_H

GLuint base;
GLuint texture[2];
GLuint loop;
GLfloat cnt1;
GLfloat cnt2;

////////////////////////////////////////////////
//Definitions For Texture.cpp Functions
////////////////////////////////////////////////

AUX_RGBImageRec *LoadBMP(char *Filename);

int LoadGLTextures();

GLvoid BuildFont(GLvoid);

GLvoid KillFont(GLvoid) ;

GLvoid glPrint(GLint x, GLint y, char *string, int set);

#endif
#5
06/14/2002 (4:20 pm)
I just want to say I am still looking for help with the above error messages :)
#6
06/14/2002 (4:25 pm)
It absolutely matters that you put implementation in the header file.

The problem is that main.cpp is including texture.h, right?

So the compiler is going to take the code in texture.h and actually compile it and put the resulting machine code into main.obj. The compiler doesn't care that the code was in an .h file and not a .cpp, it doesn't make a distinction. Then when you build any other file that includes texture.h (including Piece.cpp) its ALSO going to put the code it finds in the header file into the related .obj file for that .cpp file. Then when its time to link you're going to have a bunch of functions defined in all these obj files that all have the same signature, because they're really the same function. But the linker doesn't realize what the compiler did, so it won't just ignore the repeats (at least with default options).

Keep in mind that inclusion guards, which are a very good thing, only help you out at the preprocessing stage. So when main.cpp is being processed & compiled, texture.h will only be included once...and when Piece.cpp is being processed & compiled, texture.h will only be included once, but the CODE that exists within that header file is going to be inserted into both main.obj and piece.obj because they are preprocessed & compiled distinctly. Inclusion guards don't help later on at link time, and this is when your error will bite you on the ass.

Get the implementations out of the header files! You *CAN* put inline class method implementations in a C++ header file because the C++ standard allows for this explicitly, but putting C style functions in a header file is going to cause exactly the problem you are seeing.
#7
06/14/2002 (4:34 pm)
Thanks! I don't undertand everything you just said but I'll go back over it and look up a few things. I do get the jist of what you siad though. hehe I'm back to the fun stuff
#8
06/14/2002 (4:53 pm)
You'll understand soon enough. I think a lot of people who are learning C/C++ run into this when they first start out but later on it becomes a non-issue.

Here's a basic breakdown of what's happening in your specific case, so that its clear:

Ok, well first of all building C/C++ programs is broken down into three major steps, preprocessing, compiling and linking.

The preprocessor step is where all of the '#' stuff is resolved, like #include, #define, etc.

So the preprocess will load in main.cpp, go through it line by line, see that it #includes texture.h, so what it does is it actually merges the contents of texture.h into its in-memory version of main.cpp where the #include occured.

Let me illustrate this with some very simple psuedoish sample code:


Main.cpp
---------
#include "stuff.h"

main()
{
doSomething();
}
---------

stuff.h
----------
#ifndef STUFF_H
#define STUFF_H

doSomething()
{
printf("Hello!");
}

#endif


Ok, so the preprocessor is going to load main.cpp, see that it includes stuff.h and its going to merge the contents of stuff.h into main.cpp, the result in-memory will look like this:

------

doSomething()
{
printf("Hello!");
}

main()
{
doSomething();
}

------

The contents of stuff.h have been included into main.cpp. The #ifndef stuff has NOT been included into the in-memory version of main.cpp because these commands only make sense at the preprocessor stage, not the compile or link stage, and since the preprocessor has already done its work, there's no need for those to be around anymore.

So when the results are passed to the compiler (the next stage) all it knows is that a function named doSomething is implemented within main.cpp. It has no idea it came from texture.h or that it should only be defined once in the final program. The compiler will create a main.obj file with the machine code for main() and doSomething() in it. Later this obj file will be passed to the linker with all the other obj files that make up the full program and they will be linked to become the file exe file.

This type of header inclusion is not an issue if only one *.cpp file includes the *.h file with a function implementation, but if someone else, say "other.cpp" also includes stuff.h...

other.cpp
-------
#include "stuff.h"

doAnotherThing()
{
printf("Bye!");
}
-------

Well, other.cpp is going to be loaded by the preprocessor and the preprocessor (which no longer has any memory of what happened when it preprocessed main.cpp, its a completely different run) is going to do the same thing it did with main.cpp. So the in-memory source code for other.cpp will look like this:

-------

doSomething()
{
printf("Hello!");
}

doAnotherThing()
{
printf("Bye!");
}

-------

As with main.cpp/main.obj, the doSomething function is going to be included in the machine code...when the compiler compiles the full in-memory other.cpp, its going to write another copy of doSomething into other.obj.

Now when the linker goes to link main.obj, other.obj and whatever else to produce the final exe, its going to see that doSomething() is implemented in multiple obj files and throw a fit because it doesn't know why the code is defined twice.

This is why you should never put C function implementation into header files. Make a stuff.cpp and put the actual code for doSomething() in there, and just put the function definition/interface for it in stuff.h, because the definition is all the compiler needs to build a main.obj from main.cpp that can later link to the doSomething function, which will come in from the stuff.obj file.

That may still be a bit confusing, but as you learn more about the preprocessor, compiler and linker and how they interoperate, hopefully it will make more sense.