Go Down

Topic: multiple definition of ... (Read 7155 times) previous topic - next topic

Udo Klein

Sep 21, 2009, 11:54 pm Last Edit: Sep 22, 2009, 08:02 pm by udoklein Reason: 1
Today I tried to split my current project into some libraries. I did this according to the tutorial in the playground. It works fine as long as I do not define any variables in the .h files.

For example I have put my LCD handling into a separate .h and .cpp file like so:

Code: [Select]

#ifndef UT_LCD_h
#define UT_LCD_h

#include <LiquidCrystal.h>
#include <inttypes.h>

//create object to control void menu__init() an LCD, number of lines in display=2
//LCD4Bit_mod lcd = LCD4Bit_mod(2);
LiquidCrystal lcd(10, 11, 9, 4, 5, 6, 7);

// function definitions follow here

#endif


The cpp file includes it with
Code: [Select]

#include "UT_LCD.h"


and the main file includes them with

Code: [Select]

#include <UT_LCD.h>


When I try to compile this I always get an error

Code: [Select]

udo@laptop:~/sketchbook/UT$ make
cat build-cli/00_UT.d > build-cli/depends.mk
/usr/bin/avr-gcc -mmcu=atmega328p    -lm -Wl,--gc-sections -Os -o build-cli/00_UT.elf build-cli/00_UT.o  build-cli/pins_arduino.o  build-cli/wiring_analog.o  build-cli/wiring.o  build-cli/wiring_digital.o  build-cli/HardwareSerial.o  build-cli/LiquidCrystal.o  build-cli/Print.o  build-cli/UT_Conversion.o  build-cli/UT_Event_buffer.o  build-cli/UT_LCD.o
build-cli/UT_LCD.o: In function `lcd__init()':
arduino/UT_LCD.cpp:3: multiple definition of `lcd'
build-cli/00_UT.o:build-cli/00_UT.cpp:2693: first defined here
make: *** [build-cli/00_UT.elf] Fehler 1


Now if code
Code: [Select]

static LiquidCrystal lcd(10, 11, 9, 4, 5, 6, 7);


instead of

Code: [Select]

LiquidCrystal lcd(10, 11, 9, 4, 5, 6, 7);


everything works as expected. However the compiled code seems to get longer than it used to be before I split everything.

Since I am not a C Wizard at all I would be very thankful for any hints about what causes this behaviour and the proper solution to get rid of it.
Check out my experiments http://blog.blinkenlight.net

AlphaBeta

You could try extern.

header:
Code: [Select]
extern LiquidCrystal lcd;

cpp:
Code: [Select]
extern LiquidCrystal lcd(10, 11, 9, 4, 5, 6, 7);

Udo Klein

By now I figured out what happened. At least I think I understand it. Would anyone comment if I undestand it right:

My declaration gets compiled twice. Once into UT_LCD.cpp and once into UT_main.cpp. Obviously this clashes. If I insert a static it still gets compiled twice but visibility is limited to the different files. Still the program works because the two LCD objects have no internal state but the state in the LCD display itself. Hence I then have two different LCD instances and hence I have twice the memory usage.

So either I go to use "extern" or I put the declaration into the UT_LCD.cpp and ensure that there are suitable means to access the LCD object from main.

Is this theory right?
Check out my experiments http://blog.blinkenlight.net

Udo Klein

By now I am in more trouble than before. So far I resolved most dependencies. But now comes the hard stuff. I have UT_Code.h / UT_Code.cpp files. The idea behind them is that there are "code" constants that map 1:1 with string constants. So I pass around the uint8_t codes and convert them to strings only when I need to output them.

Code: [Select]

#ifndef UT_Code_h
#define UT_Code_h

#include <inttypes.h>
#include <avr/pgmspace.h>
#include <string.h>

const uint8_t c_code_off = 0;
const uint8_t c_code_on  = 1;

char cs_on[]  PROGMEM = "on";
char cs_off[] PROGMEM = "off";
PGM_P string_table_on_off[] PROGMEM = { cs_on, cs_off };

#endif


Of course this is not the only such declaration, actually it contains lots of such constant and string declarations.

Of course this included by UT_Code.cpp. But it is also included by
some other file "UT_Field.h" and both are included by "UT_Main.cpp".

Now this lets the compiler again complain a lot about multiple definition of these constants.

To be more precise it complains about the line
Code: [Select]

PGM_P string_table_on_off[] PROGMEM = { cs_on, cs_off };


It does not help at all to rewrite this like
Code: [Select]

extern PGM_P string_table_on_off[] PROGMEM = { cs_on, cs_off };


Has anybody an idea how to fix this? I am definitely confused :(
Check out my experiments http://blog.blinkenlight.net

jabber

#4
Sep 22, 2009, 07:48 pm Last Edit: Sep 22, 2009, 08:23 pm by jabber Reason: 1
I'n no C programmer but wouldn't this help
Code: [Select]

#ifndef UT_LCD_h
#include UT_LCD_h


for the includes.

Plus a bit more obviously  :)

Edit : I meant
#include <UT_LCD.h>

Udo Klein

No, this is what I already did. It did not help. Still I would like to learn if my theory is right. Any comments on this? For the progmem stuff: I resolved it basically the same way. That is I split the includes into the constants and the string constants part and include the string constants in the cpp only. This works but I do not really like it though.

Here it starts to bite back that I am not a C Expert and did not bother to structure my files well in advance. Now I have to untangle the whole mess and learn about the C header file techniques and all visibility / declaration rules.  :(
Anyway I expect to finish this exercise this week or the next week.

I am already down to less than 30 global variables  :)
Check out my experiments http://blog.blinkenlight.net

AlphaBeta

#6
Sep 22, 2009, 10:27 pm Last Edit: Sep 22, 2009, 10:36 pm by AlphaBeta Reason: 1
What are the contents of you files?

Might I see it all? (I meant we, of cource)

http://arduino.pastebin.com



This might be obvious, but declaring variables in a header does not imply that they are not global. :)

[edit]And, if by any chance your goal is to being able to use variables declared in the header in any file that includes it, the answer is already given here  8-) (if you do not define the int in a source that #includes your header with the declaration, you'll get an 'already defined')

That is:
You need both a declaration (extern int x;) and a definition (extern int x = 12;)[/edit]

Udo Klein

All 42 files??? Even with the paste bin this would be a little bit to much I think. I would just like to learn if I understood the meaning of extern properly. And maybe a hint why this does not work with the PGM_P stuff. With regard to my issues: as I said I finally fixed them.

And yeah: I understand that global variables are global even if I shift them to different .h files. However this is always only the first step. Once I have separated them from main I push them to the .cpp files. Then they cease to be global. The issue is then to sort out how the other references to these variables can be made working again.

Right now I use extern declarations and then replace them one after another with accessor functions. Typically I make them "semantic" instead of setter/getters.

Cheers, Udo
Check out my experiments http://blog.blinkenlight.net

aerodyno

I'm running in to similar issues.  It's as if the compiler is ignoring my "#IFNDEF" directive -- it seems like it's compiling the same stuff twice and then complaining about multiple definitions. Maybe this is an issue introduced in the 0017 arduino build? Somebody else had a similar complaint.

I too am trying to put variables in a header file and access them from different files.

Udo Klein

It is definitely not an issue specific to Arduino 17. Reason: I do not use the IDE and I do not use any of the original libraries anymore. I only use the bootloader and some heavily patched libraries. So this is definitely not caused by Arduino 17. It has to do with the way C compiles and links. Obviously I am lacking complete understanding (but I am working to fix this).

--> The issue is not a bug but a feature. The question is how to figure it out  :)
Check out my experiments http://blog.blinkenlight.net

mellis

If you try to assign a value to the variable in the .h, it's a definition (e.g. allocating space for the variable).  You need something like this in the .h file:

extern PGM_P string_table_on_off[] PROGMEM;

with the initializer in {} only in the .c / .cpp file.

Udo Klein

Ah, now this is helpful advice  :)

Just one more detail question. So the compiler does not complain about the multiple definition but about the multiple initialization. But what I still not understand is the following effect I noticed:

If I have a function definition (say void a();) in the same header file after the offending initialization, then the compiler does not complain about the definition of a. If I move it in front of the initialization then it will complain about multiple definition of a(). But if I them remove the initialization (of the variable), then the compiler will stop complaining about multiple definition of a().

This looks strange to me and I do not really understand why this happens. It's not a big deal because I will (from now on) separate the definitions from the initialization as you recommend. But I would like to understand why this happens. Do you have any hints on this?
Check out my experiments http://blog.blinkenlight.net

AlphaBeta

And we're back to reply one. :)

Variable definition is variable initialization.

Variable declaration is not variable initialization.

Quote
You could try extern.

header:
Code: [Select]
extern LiquidCrystal lcd; //this is the declaration

cpp:
Code: [Select]
extern LiquidCrystal lcd(10, 11, 9, 4, 5, 6, 7);//this is the variable definition


Every variable need both, once.
You can declare and define at the same time, for locally/globally scoped variables, but normally one declares a variable in the header, and define the variable in a source file.

:)

Udo Klein

By now I think I understand the difference between declaration and definition completely. And of course I understand why the compiler complains.

But what I still do not understand: why does it complain about a function declaration depending on the order I declare it? I would expect that it does not matter how I order it with the offending variable definition. But it does. Any hints on this part?
Check out my experiments http://blog.blinkenlight.net

AlphaBeta

The order should have no impact.


Could you provide us an example that reproduces the error you speak of?

Go Up