Author Topic: Webbotlib - Compiling with multiple .c files referring to the same .h file  (Read 4452 times)

0 Members and 1 Guest are viewing this topic.

Offline GertlexTopic starter

  • Supreme Robot
  • *****
  • Posts: 763
  • Helpful? 24
  • Nuclear Engineer · Roboticist
    • Index of Oddities
So I'm running into the semi-infamous "multiple definition"/"first defined here" errors.

My preference is to have separate C files for some chunks of code.  I'm perfectly OK with rebuilding the project in Project designer when I change out .C files (so that the makefile gets updated)... But I inevitably run into problems with the header files if I have multiple .c files #include the same .h file. (but this doesn't happen with hardware.h...)

Webbot was against this type of program structure, about two years ago.  Is this still the case?

I've included below the preprocessor commands at the top of my files that I use to try and make this work...

(the main file) NumaWP2.c:
Code: [Select]
#include "hardware.h"
#include <math.h>
#include "Maths/Vector2D.h"
#include <Gait/GaitRunner.h>

#ifndef SERVOPOSCOUNTS_LOADED
#include "servoPosConsts.h"
#endif

#ifndef Commander_h
#include "Commander.h"
#endif

#include "gait2.h"
~code...
//end of file

Commander.c:
Code: [Select]
#include "hardware.h"

#ifndef SERVOPOSCOUNTS_LOADED
#include "servoPosConsts.h"
#endif

#ifndef Commander_h
#include "Commander.h"
#endif
~code...
//end of file

Commander.h:
Code: [Select]
#ifndef Commander_h
#define Commander_h
~~~a bit of stuff here
#endif//end of file

ServoPosConsts.h:
Code: [Select]
#ifndef SERVOPOSCOUNTS_LOADED
#define SERVOPOSCOUNTS_LOADED
~~~lots of stuff here
#endif //end of file
This all works fine if I don't include servoPosConsts.h in Commander.c....

I guess there's something fundamental missing in my understanding here.  I can't see what hardware.h does that I'm not doing that protects me (aka I don't get errors) from including it multiple time.

I can share the whole project directory if that will help someone diagnose what I'm messing up...

Thanks
I

Offline Admin

  • Administrator
  • Supreme Robot
  • *****
  • Posts: 11,703
  • Helpful? 173
    • Society of Robots
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #1 on: December 09, 2011, 02:57:11 AM »
Quote
My preference is to have separate C files for some chunks of code.
To avoid compiler confusion, WebbotLib is designed to only work with one .c file. Change your other .c files to .h files and all will be well :P

Offline GertlexTopic starter

  • Supreme Robot
  • *****
  • Posts: 763
  • Helpful? 24
  • Nuclear Engineer · Roboticist
    • Index of Oddities
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #2 on: December 09, 2011, 07:06:31 AM »
What does Webbotlib do that breaks the functionality?
I

Offline Admin

  • Administrator
  • Supreme Robot
  • *****
  • Posts: 11,703
  • Helpful? 173
    • Society of Robots
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #3 on: December 09, 2011, 07:33:41 AM »
I think Webbot can answer this better, but basically your compiler will get confused when you have file A referencing file B which references file C which references file A. It's not smart enough to know when a file has already been referenced, giving you that multiple definition error. By having one .c, it knows the 'master' file.

(FYI, I'm sure a real professional programmer can answer this better  :P)

Offline GertlexTopic starter

  • Supreme Robot
  • *****
  • Posts: 763
  • Helpful? 24
  • Nuclear Engineer · Roboticist
    • Index of Oddities
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #4 on: December 09, 2011, 10:39:16 AM »
I'm no pro programmer either, but my understanding is that preprocessor commands (all the code stuff I put in my original post) are supposed to get around this issue.  I'm half expecting the answer to my problems is that I did the preprocessor stuff incorrectly.  Thanks though :)
I

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
    • Webbot stuff
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #5 on: December 09, 2011, 12:54:57 PM »
Whats the error you get - ie what is 'multi-defined'?
Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline GertlexTopic starter

  • Supreme Robot
  • *****
  • Posts: 763
  • Helpful? 24
  • Nuclear Engineer · Roboticist
    • Index of Oddities
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #6 on: December 09, 2011, 01:12:39 PM »
Guess I might as well post the entire error output. Should have at least mentioned that it specifically complains about multiple defintions of stuff in servoPosConsts.h.

Code: [Select]
Build started 9.12.2011 at 12:57:58
avr-gcc -g -Wall -DF_CPU=16000000 -mmcu=atmega640 -gdwarf-2 -fpack-struct -fshort-enums  -funsigned-char \
-funsigned-bitfields -I"../../../WebbotLib/2.08"  -MD -MP -MT NumaWB2.o -MF dep/NumaWB2.o.d  -std=gnu99  -O0 -c -o NumaWB2.o NumaWB2.c
avr-gcc -mmcu=atmega640 -Wl,-Map,NumaWB2.map,-u,vfprintf -lprintf_flt -o NumaWB2.elf servoPosConsts.o \
Commander.o NumaWB2.o  lib/lib_iopins.o lib/lib_hardware.o lib/gen_clock.o lib/lib_timerdef.o lib/lib_timers.o  -L"../../../WebbotLib/2.08" -L"lib" -lWebb
ot-ATmega640  -lPost -lm -lc

NumaWB2.o:(.data+0x0): multiple definition of `s11pos'
Commander.o:(.data+0x0): first defined here
NumaWB2.o:(.data+0x2): multiple definition of `s12pos'
Commander.o:(.data+0x2): first defined here
NumaWB2.o:(.data+0x4): multiple definition of `s13pos'
Commander.o:(.data+0x4): first defined here
NumaWB2.o:(.data+0x6): multiple definition of `s14pos'
Commander.o:(.data+0x6): first defined here
NumaWB2.o:(.data+0x8): multiple definition of `s21pos'
Commander.o:(.data+0x8): first defined here
NumaWB2.o:(.data+0xa): multiple definition of `s22pos'
Commander.o:(.data+0xa): first defined here
NumaWB2.o:(.data+0xc): multiple definition of `s23pos'
Commander.o:(.data+0xc): first defined here
NumaWB2.o:(.data+0xe): multiple definition of `s24pos'
Commander.o:(.data+0xe): first defined here
NumaWB2.o:(.data+0x10): multiple definition of `s31pos'
Commander.o:(.data+0x10): first defined here
NumaWB2.o:(.data+0x12): multiple definition of `s32pos'
Commander.o:(.data+0x12): first defined here
NumaWB2.o:(.data+0x14): multiple definition of `s33pos'
Commander.o:(.data+0x14): first defined here
NumaWB2.o:(.data+0x16): multiple definition of `s34pos'
Commander.o:(.data+0x16): first defined here
NumaWB2.o:(.data+0x18): multiple definition of `s41pos'
Commander.o:(.data+0x18): first defined here
NumaWB2.o:(.data+0x1a): multiple definition of `s42pos'
Commander.o:(.data+0x1a): first defined here
NumaWB2.o:(.data+0x1c): multiple definition of `s43pos'
Commander.o:(.data+0x1c): first defined here
NumaWB2.o:(.data+0x1e): multiple definition of `s44pos'
Commander.o:(.data+0x1e): first defined here
NumaWB2.o:(.data+0x20): multiple definition of `s51pos'
Commander.o:(.data+0x20): first defined here
NumaWB2.o:(.data+0x22): multiple definition of `s52pos'
Commander.o:(.data+0x22): first defined here
NumaWB2.o:(.data+0x24): multiple definition of `servo11Min'
Commander.o:(.data+0x24): first defined here
NumaWB2.o:(.data+0x26): multiple definition of `servo11Max'
Commander.o:(.data+0x26): first defined here
NumaWB2.o:(.data+0x28): multiple definition of `servo12Min'
Commander.o:(.data+0x28): first defined here
NumaWB2.o:(.data+0x2a): multiple definition of `servo12Max'
Commander.o:(.data+0x2a): first defined here
NumaWB2.o:(.data+0x2c): multiple definition of `servo13Min'
Commander.o:(.data+0x2c): first defined here
NumaWB2.o:(.data+0x2e): multiple definition of `servo13Max'
Commander.o:(.data+0x2e): first defined here
NumaWB2.o:(.data+0x30): multiple definition of `servo14Min'
Commander.o:(.data+0x30): first defined here
NumaWB2.o:(.data+0x32): multiple definition of `servo14Max'
Commander.o:(.data+0x32): first defined here
NumaWB2.o:(.data+0x34): multiple definition of `servo21Min'
Commander.o:(.data+0x34): first defined here
NumaWB2.o:(.data+0x36): multiple definition of `servo21Max'
Commander.o:(.data+0x36): first defined here
NumaWB2.o:(.data+0x38): multiple definition of `servo22Min'
Commander.o:(.data+0x38): first defined here
NumaWB2.o:(.data+0x3a): multiple definition of `servo22Max'
Commander.o:(.data+0x3a): first defined here
NumaWB2.o:(.data+0x3c): multiple definition of `servo23Min'
Commander.o:(.data+0x3c): first defined here
NumaWB2.o:(.data+0x3e): multiple definition of `servo23Max'
Commander.o:(.data+0x3e): first defined here
NumaWB2.o:(.data+0x40): multiple definition of `servo24Min'
Commander.o:(.data+0x40): first defined here
NumaWB2.o:(.data+0x42): multiple definition of `servo24Max'
Commander.o:(.data+0x42): first defined here
NumaWB2.o:(.data+0x44): multiple definition of `servo31Min'
Commander.o:(.data+0x44): first defined here
NumaWB2.o:(.data+0x46): multiple definition of `servo31Max'
Commander.o:(.data+0x46): first defined here
NumaWB2.o:(.data+0x48): multiple definition of `servo32Min'
Commander.o:(.data+0x48): first defined here
NumaWB2.o:(.data+0x4a): multiple definition of `servo32Max'
Commander.o:(.data+0x4a): first defined here
NumaWB2.o:(.data+0x4c): multiple definition of `servo33Min'
Commander.o:(.data+0x4c): first defined here
NumaWB2.o:(.data+0x4e): multiple definition of `servo33Max'
st defined here
NumaWB2.o:(.data+0x50): multiple definition of `servo34Min'
Commander.o:(.data+0x50): first defined here
NumaWB2.o:(.data+0x52): multiple definition of `servo34Max'
Commander.o:(.data+0x52): first defined here
NumaWB2.o:(.data+0x54): multiple definition of `servo41Min'
Commander.o:(.data+0x54): first defined here
NumaWB2.o:(.data+0x56): multiple definition of `servo41Max'
Commander.o:(.data+0x56): first defined here
NumaWB2.o:(.data+0x58): multiple definition of `servo42Min'
Commander.o:(.data+0x58): first defined here
NumaWB2.o:(.data+0x5a): multiple definition of `servo42Max'
Commander.o:(.data+0x5a): first defined here
NumaWB2.o:(.data+0x5c): multiple definition of `servo43Min'
Commander.o:(.data+0x5c): first defined here
NumaWB2.o:(.data+0x5e): multiple definition of `servo43Max'
Commander.o:(.data+0x5e): first defined here
NumaWB2.o:(.data+0x60): multiple definition of `servo44Min'
Commander.o:(.data+0x60): first defined here
NumaWB2.o:(.data+0x62): multiple definition of `servo44Max'
Commander.o:(.data+0x62): first defined here
NumaWB2.o:(.data+0x64): multiple definition of `servo11Ang'
Commander.o:(.data+0x64): first defined here
NumaWB2.o:(.data+0x68): multiple definition of `servo21Ang'
Commander.o:(.data+0x68): first defined here
NumaWB2.o:(.data+0x6c): multiple definition of `servo31Ang'
Commander.o:(.data+0x6c): first defined here
NumaWB2.o:(.data+0x70): multiple definition of `servo41Ang'
Commander.o:(.data+0x70): first defined here
NumaWB2.o: In function `appInitHardware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:86: multiple definition of `cos_servo11Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:11: first defined here
NumaWB2.o: In function `appInitHardware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:86: multiple definition of `sin_servo11Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:11: first defined here
NumaWB2.o: In function `appInitHardware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:87: multiple definition of `cos_servo21Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:11: first defined here
NumaWB2.o: In function `appInitHardware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:88: multiple definition of `sin_servo21Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:11: first defined here
NumaWB2.o: In function `appInitHardware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:88: multiple definition of `cos_servo31Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:14: first defined here
NumaWB2.o: In function `appInitHardware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:89: multiple definition of `sin_servo31Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:14: first defined here
NumaWB2.o: In function `appInitHardware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:89: multiple definition of `cos_servo41Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:14: first defined here
NumaWB2.o: In function `appInitSoftware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:92: multiple definition of `sin_servo41Ang'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:14: first defined here
NumaWB2.o:(.data+0x74): multiple definition of `bodyH'
Commander.o:(.data+0x74): first defined here
NumaWB2.o:(.data+0x76): multiple definition of `L0'
Commander.o:(.data+0x76): first defined here
NumaWB2.o: In function `appInitSoftware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:92: multiple definition of `y11'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:15: first defined here
NumaWB2.o: In function `appInitSoftware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:92: multiple definition of `y21'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:15: first defined here
NumaWB2.o: In function `appInitSoftware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:92: multiple definition of `y31'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:15: first defined here
NumaWB2.o: In function `appInitSoftware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:92: multiple definition of `y41'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:15: first defined here
NumaWB2.o:(.data+0x78): multiple definition of `L12'
Commander.o:(.data+0x78): first defined here
7a): multiple definition of `L23'
Commander.o:(.data+0x7a): first defined here
NumaWB2.o:(.data+0x7c): multiple definition of `L34'
Commander.o:(.data+0x7c): first defined here
NumaWB2.o:(.data+0x7e): multiple definition of `L45'
Commander.o:(.data+0x7e): first defined here
NumaWB2.o:(.data+0x80): multiple definition of `sqL23'
Commander.o:(.data+0x80): first defined here
NumaWB2.o:(.data+0x82): multiple definition of `sqL34'
Commander.o:(.data+0x82): first defined here
NumaWB2.o:(.data+0x84): multiple definition of `x11'
Commander.o:(.data+0x84): first defined here
NumaWB2.o:(.data+0x86): multiple definition of `x21'
Commander.o:(.data+0x86): first defined here
NumaWB2.o:(.data+0x88): multiple definition of `x31'
Commander.o:(.data+0x88): first defined here
NumaWB2.o:(.data+0x8a): multiple definition of `x41'
Commander.o:(.data+0x8a): first defined here
NumaWB2.o: In function `appInitSoftware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:92: multiple definition of `offsetServo1'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:15: first defined here
NumaWB2.o:(.data+0x8c): multiple definition of `offsetServo2'
Commander.o:(.data+0x8c): first defined here
NumaWB2.o:(.data+0x8e): multiple definition of `offsetServo3'
Commander.o:(.data+0x8e): first defined here
NumaWB2.o: In function `appInitSoftware':
C:\Sync\AxonWork\NumaSVNC/NumaWB2.c:92: multiple definition of `offsetServo4'
Commander.o:C:\Sync\AxonWork\NumaSVNC/Commander.c:15: first defined here
NumaWB2.o:(.data+0x90): multiple definition of `d_tilt'
Commander.o:(.data+0x90): first defined here
NumaWB2.o:(.data+0x92): multiple definition of `d_pan'
Commander.o:(.data+0x92): first defined here
NumaWB2.o:(.data+0x94): multiple definition of `tilt_pos'
Commander.o:(.data+0x94): first defined here
NumaWB2.o:(.data+0x96): multiple definition of `pan_pos'
Commander.o:(.data+0x96): first defined here
NumaWB2.o:(.data+0x98): multiple definition of `servo51Min'
Commander.o:(.data+0x98): first defined here
NumaWB2.o:(.data+0x9a): multiple definition of `servo51Max'
Commander.o:(.data+0x9a): first defined here
NumaWB2.o:(.data+0x9c): multiple definition of `servo52Min'
Commander.o:(.data+0x9c): first defined here
NumaWB2.o:(.data+0x9e): multiple definition of `servo52Max'
Commander.o:(.data+0x9e): first defined here
make: *** [NumaWB2.elf] Error 1
Build failed with 1 errors and 0 warnings...
I

Offline Webbot

  • Expert Roboticist
  • Supreme Robot
  • *****
  • Posts: 2,165
  • Helpful? 111
    • Webbot stuff
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #7 on: December 09, 2011, 02:15:47 PM »
Ok - thats clearer. Long explanation coz its a C thing rather than a WebbotLib thing. Here is what is happening....

In C you should only declare the body of a function, or create a variable, with the same name ONCE.  The best practice is that you do this inside a C file and then you create a H file to expose the functions/variables to the outside world (ie other code) via a .H file.

EXAMPLE OF BAD STUFF
---- test.h ----
Code: [Select]
int foo=1; // Declare a variable

void bar(int p){ // Declare a function
   -- do something --
}


--- test1.c ----
Code: [Select]
#include "test.h"
void something1(void){
    bar(foo);
}

--- test2.c ----
Code: [Select]
#include "test.h"
void something2(void){
    bar(foo);
}

This will compile the individual C files into O files without errors. Effectively:-
  • test1.o will contain the code to define the foo variable and the  bar function and the something1 function. The something1 function accesses both foo and bar.
  • test2.o will contain the code to define the foo variable and the  bar function and the something2 function. The something2 function accesses both foo and bar.
But when the linker tries to amalgamate test1.o and test2.o to produce the whole program it gives you a multi-defined error nas you now have 2 things called 'foo' and two things called 'bar'. Since these are both accessed by the 2 C files then it has no idea which one the C files is trying to use.
Wrapping stuff with #ifdef pre-processor commands ain't gonna help since each C file is compiled as a standalone thing. ie when compiling test2.c the compiler has no idea that test.h was used by test1.c

SO HOW DO YOU FIX IT
foo and bar are accessed by various C files so they should be placed into a new C file - lets call it test.c
---- test.c ----
Code: [Select]
int foo=1; // Declare a variable

void bar(int p){ // Declare a function
   -- do something --
}

Then change the H file to let all other C files know that these variable exist somewhere....
---- test.h ----
Code: [Select]
extern int foo; // Let the compiler know that there is an int variable called foo that has been declared externally (ie somewhere else)

// Also let it know that there is a function called 'bar' that is defined somewhere else.
// Note that the keyword 'extern' is optional for a function.
// So long as you dont supply the body of the function (ie you just have a ;) then it knows that its defined somewhere else.
extern void bar(int p);


This will compile the individual C files into O files without errors. Effectively:-
  • test.o will contain the code to define the foo variable and the  bar function.
  • test1.o will contain the code for the something1 function. It uses foo and bar but knows they exist elsewhere.
  • test2.o will contain the code for the something2 function. It uses foo and bar but knows they exist elsewhere.
The linker will now stitch the 3 .o files together quite happily as everything has only been defined once.

WHAT ABOUT 'static' AND '__inline__' (Advanced Topic!!)
The __inline__ keyword is often used at the start of a function declaration in H files  (look at the WebbotLib files). It gives a hint to the compiler that this function is a candidate to be used inline. Which basically means 'dont generate the code as a standard function but rather, every time it is called, then cut and paste the code from the function to the place that is calling it'. Why would you do this? Well one benefit is that if the code is never called then no code ever gets generated so your program is smaller. Also: it can never be multi-defined since the function doesn't exist as a stand alone thing.But you need to be careful: if the body of the function is quite large and is used quite often then its like cut'n'pasting the code all over the place - so your program gets very large. Thats why I called it a 'hint' since depending on various factors: including the optimise for speed vs optimise for size settings the compiler may choose to ignore your hint all together and still create it as a standalone function. In which case: and you've called it from lots of C files, then each C file now has a function with that name so its back to the linker bombing out with a multi-defined error. The cure is to use 'static' ....

The static keyword in front of a function declaration or variable declaration means that this function/variable is only visible within the current C file and its name is hidden from the linker. So if you declare something in an H file as static and include the H file from, say, 3 C files then the code for the function, or variable, will exist 3 times. You won't get a multi-defined error as each one is hidden from the other in the linker. But this makes it impossible for test1.c to read/write/call the variable/function with the same name in any other C file. This is a good thing! It means that if you have a C file to do a specific job and you have a variable called 'myVar' which is only ever used by the code in that file, and you dont want to have other C files accidentally stuffing its value, then you can declare it as static. But also beware: this only applies to variables defined outside of functions - declaring a variable as static inside a function has a whole different meaning....




WEBBOTLIB
Looking at your error log then some of the stuff looks like your code and its hard to tell but some of the stuff may be things you declared in Project Designer but I'm not sure.

If you are using WebbotLib V1 then make sure you only ever include 'hardware.h' from your main C file so that your devices are only defined once. All other C files should include 'xhardware.h'. This version of the file declares all the devices as being 'extern' to stop them being multi defined. Version 2 doesn't need you to do this.


KISS
This added complexity, bought about by the C language, has resulted in the general KISS principal of having one C file and having everything else in H files. That way nothing can EVER be multi-defined as everything is effectively just one file that is cut'n'past together. Follow the above steps and you can split stuff out. WebbotLib has no problem with it - the only observation is to make sure, in Version 1, that you only include hardware.h once (from your main C file) and use xhardware.h in other C files.

Webbot Home: http://webbot.org.uk/
WebbotLib online docs: http://webbot.org.uk/WebbotLibDocs
If your in the neighbourhood: http://www.hovinghamspa.co.uk

Offline GertlexTopic starter

  • Supreme Robot
  • *****
  • Posts: 763
  • Helpful? 24
  • Nuclear Engineer · Roboticist
    • Index of Oddities
Re: Webbotlib - Compiling with multiple .c files referring to the same .h file
« Reply #8 on: December 09, 2011, 03:01:17 PM »
 :D :D :D
Thanks a ton for clarifying all of that, Webbot.  My code now compiles and is structured in a way that pleases me :)
I

 


Get Your Ad Here