Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » allegro.h desperation: multiple definitions

This thread is locked; no one can reply to it. rss feed Print
allegro.h desperation: multiple definitions
Paul Wilhelm
Member #10,731
February 2009

Good evening Allegro community!

To introduce myself first: my Name is Paul (19 at this time) and I'm from Germany.
I've been programming in QB and Assembler since 2001, learned some PHP later (see: http://mosfetkiller.de/) and finally learned C like 2 years ago.
Thanks to Allegro, I rediscovered the joy of game programming again. :-)

Nevertheless, I am definately not a C expert yet.
I wrote a basic RPG engine several days ago, which worked so far.
But it was just one huge "main.c" file, so I decided to split it in parts like "video.c", "logic.c" and so on.

Unfortunately I have some serious problems with my header files.

Since every module ("main.c", "video.c", "logic.c", ...) accesses functions of the Allegro library, I have to include "allegro.h" in every single header file of all the modules.

But when I compile, I get a huge amount of "multiple definition" errors.
What am I doing wrong?

What is the right way of using header files?

Believe me, for the last two days I tried everything possible to get the whole thing to work, but I just couldn't make it. :-/

I really hope, someone here can help me. Thanks!

Greetings from Germany,
Paul

______________________________________________
mosfetkiller.de - high voltage, electronics, programming

OnlineCop
Member #7,919
October 2006
avatar

If you're reusing global variables in more than a single .C file, your compiler will complain.

If you have a global that you will use in multiple places, pick one of the files to be its "official declaration" file. Everywhere else, define those variables as "extern".

main.c:

  int MAP_WIDTH;
  int MAP_HEIGHT;

video.c:

  extern int MAP_WIDTH;
  extern int MAP_HEIGHT;

logic.c:

  // same as in video.c:
  extern int MAP_WIDTH;
  extern int MAP_HEIGHT;

That sounds like one of your bigger problems.

If you use header files, MAKE SURE that you put in header guards. At the top of those header files, you want to use the preprocessor "#ifndef" checks:

main.h:

  #ifndef MAIN_H
  #define MAIN_H

  // Everything found within your main.h file as normal here...

  #endif

Now, it doesn't matter WHICH source files are trying to include that header file... when it gets compiled and linked, it will only include it once.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Quote:

If you have a global that you will use in multiple places, pick one of the files to be its "official declaration" file. Everywhere else, define those variables as "extern".

main.c:

int MAP_WIDTH;
int MAP_HEIGHT;

video.c:

extern int MAP_WIDTH;
extern int MAP_HEIGHT;

logic.c:

// same as in video.c:
extern int MAP_WIDTH;
extern int MAP_HEIGHT;

No, that's not correct. If you need to access data from outside the source file, you need to include the header that declares it, and in that header is where it should be declared as extern. Then, define that data in exactly one source file. The header makes it available for reference, the source file gives it a place to be stored. When the multiple object files consisting of the project are linked together there will only be one actual definition then.

global_data.h

//

#ifndef global_data_H
#define global_data_H

#include "Settings_struct.h"

extern Settings settings;
// declare other globally accessible data as extern here

#endif // global_data_H

//

global_data.c

//

Settings settings;

//

main.c

//
#include "global_data.h"

/// In main...
   LoadSettings(&settings , "config_file.txt");

//

LennyLen
Member #5,313
December 2004
avatar

Quote:

No, that's not correct. If you need to access data from outside the source file, you need to include the header that declares it, and in that header is where it should be declared as extern. Then, define that data in exactly one source file. The header makes it available for reference, the source file gives it a place to be stored. When the multiple object files consisting of the project are linked together there will only be one actual definition then.

Both ways are equally correct.

Thomas Fjellstrom
Member #476
June 2000
avatar

Quote:

Both ways are equally correct.

Both ways look identical to the compiler.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

LennyLen
Member #5,313
December 2004
avatar

Quote:

Both ways look identical to the compiler.

Yea, I should have extended that a bit: Both ways are equally correct in that to the comiler they're the same so the only real difference is in programming style.

I do it a similar way to Edgar. I have a "globals.h" file that contains all the extern declarations. I put the non-extern declarations in main.c and include globals.h in any other .c file that needs access to any of the globals. Header guards prohibit multiple declarations.

EDIT: N.b. I sometimes use declaration when I mean definition, and vice versa.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Well, I guess that method would work, but it's not good practice to do that manually. The extern declaration belongs in a header because it gives you the interface. When the preprocessor runs, the effect would be the same but it's bound to give you problems trying to remember to manually add every external symbol to every file you want to access them in. Putting them into a header also eliminates unnecessary code replication as well.

LennyLen
Member #5,313
December 2004
avatar

Quote:

Well, I guess that method would work

Just for clarification, I assume that by "that method" you mean what OnlineCop described, since the way that I mentioned does put the extern declarations in a header file.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I suppose I should have been more explicit then. Yes, adding an extern declaration to each source file manually will work, but it is better practice to do that by including a header containing the extern declarations instead.

Johan Halmén
Member #1,550
September 2001

If one of the multiple definitions is BITMAP, remember to do this.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

Paul Wilhelm
Member #10,731
February 2009

Thanks for your quick replies and the clarifications on the whole extern / global declarations topic.

But that doesn't seem to be my problem, I am sorry if I didn't express myself good enough.

GCC complains about the multiple inclusion of allegro.h.

Let me give you an example, which describes exactly my problem:

main.c:

#include "main.h"

int main(void)
{
    return 0;
}

main.h:

#ifndef MAIN_H_INCLUDED
#define MAIN_H_INCLUDED

#include <allegro.h>

int abc = KEY_UP;

#endif

init.c:

#include "init.h"

void foo()
{
}

init.h:

#ifndef INIT_H_INCLUDED
#define INIT_H_INCLUDED

#include <allegro.h>

BITMAP *noob;

void foo();

#endif

I am compiling this with gcc -std=gnu99 -Wall -O2 `allegro-config --libs` main.c init.c -o main and I get a huge list of multiple definition errors:

/tmp/ccU6qa1p.o: In function `bmp_read24':
init.c:(.text+0x0): multiple definition of `bmp_read24'
/tmp/cctySkfU.o:main.c:(.text+0x0): first defined here
/tmp/ccU6qa1p.o: In function `bmp_write24':
init.c:(.text+0x20): multiple definition of `bmp_write24'
/tmp/cctySkfU.o:main.c:(.text+0x20): first defined here

Like I said in my first post, both modules make use of the Allegro library.
(Of course the example doesn't make much sense since main.c makes no use of init.c - but it is just a simplified example.)
But how to include allegro.h in both header files without getting the above errors?

Greetings,
Paul

______________________________________________
mosfetkiller.de - high voltage, electronics, programming

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

There is one problem in your main.h header. It defines an int named abc instead of declaring it. If more than one source module includes main.h, then each will have defined their own 'int abc'. Then when they are linked together it will give you a redefinition error for it. Similarly, your init.h header does the same thing with your 'BITMAP* noob' variable definition.

The redefinition errors you are getting don't seem related though. Try moving the `allegro-config --libs` option to the end of the compilation command.

Paul Wilhelm
Member #10,731
February 2009

That is the thing. I am now actually only compiling these two exact files, which both don't include each others header files.

I am aware of the problem you just described, when other modules include the same definitions.

I moved the allegro-config option to the end of the command, like you said, but it doesn't seem to matter.

I really don't know what to do... :-/

______________________________________________
mosfetkiller.de - high voltage, electronics, programming

LennyLen
Member #5,313
December 2004
avatar

Your code is fine, as I was able to build it with no errors (except for the missing END_OF_MAIN() which I needed since I'm on Windows).

Try comiling the sources individually and link the object files with the allegro library.

EDIT:

I jumped to the command line, and the following worked for me:

gcc -o main.exe main.c init.c -lalleg -Wall -O2 -std=gnu99

If you replace -lalleg with `allegro-config --libs`, does it work?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Well, those are linking errors, not compilation errors, so perhaps `allegro-config --libs` is not giving you the correct output somehow.

Start from the beginning - post which OS and OS version you are compiling on, as well as which version of Allegro you are using. Post the output of `allegro-config --libs` by itself as well.

Paul Wilhelm
Member #10,731
February 2009

Allegro Version: 4.2.2
Output of allegro-config --libs: -L/usr/lib -Wl,-Bsymbolic-functions -lalleg-4.2.2

I am using Ubuntu 8.10 (intrepid).

If I replace the allegro-config parameter by -lalleg, I get even more error output.

EDIT: WOOT! I experimented, and it works when I leave out the -std=gnu99.

Any explanations? Thank you all for your help so far. :-)

______________________________________________
mosfetkiller.de - high voltage, electronics, programming

Thomas Fjellstrom
Member #476
June 2000
avatar

Quote:

Any explanations?

IIRC C99 changes how "extern inline" (or something like that) works.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

LennyLen
Member #5,313
December 2004
avatar

Quote:

IIRC C99 changes how "extern inline" (or something like that) works.

So why did it compile without a problem for me with that option? Some difference in the Windows and Linux builds of Allegro?

Paul Wilhelm
Member #10,731
February 2009

LennyLen: Which GCC version are you using?

Mine is 4.3.2. I just read that gcc does not implement C99 completely yet.
Maybe you use a different version of GCC in which it coincidentally works...but that would be very strange.

However, I'm using the C89 standard now. Now I can't do cool stuff like for (int a = 0; ...) anymore, but I can live with that.

Thanks so far!

______________________________________________
mosfetkiller.de - high voltage, electronics, programming

LennyLen
Member #5,313
December 2004
avatar

Quote:

Mine is 4.3.2. I just read that gcc does not implement C99 completely yet.
Maybe you use a different version of GCC in which it coincidentally works...but that would be very strange.

I'm using GCC 3.4.5, which is the current Windows version. MinGW is getting a little bit behind - even the WIP branch is only GCC 4.3.0.

OnlineCop
Member #7,919
October 2006
avatar

LennyLen said:

I'm using GCC 3.4.5, which is the current Windows version...

Are you sure? My mingw version shows:

gcc (GCC) 4.3.0 20080305 (alpha-testing) mingw-20080502
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

g++ (GCC) 4.3.0 20080305 (alpha-testing) mingw-20080502
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

gnolam
Member #2,030
March 2002
avatar

Quote:

gcc (GCC) 4.3.0 20080305 (alpha-testing) mingw-20080502

Emphasis added.

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Go to: