allegro.h desperation: multiple definitions
Paul Wilhelm

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

OnlineCop

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

Both ways are equally correct.

Both ways look identical to the compiler.

LennyLen
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

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

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

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

Paul Wilhelm

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

Edgar Reynaldo

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

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... :-/

LennyLen

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

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

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. :-)

Thomas Fjellstrom
Quote:

Any explanations?

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

LennyLen
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

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!

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

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

Emphasis added.

Thread #599393. Printed from Allegro.cc