Struggling on #include directive

Hello,
I’m struggling using #include directive and on this error at the end of compiling session
After the error description the very simple code used by me

Processing adafruit_feather_m4 (platform: atmelsam; board: adafruit_feather_m4; framework: arduino)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelsam/adafruit_feather_m4.html
PLATFORM: Atmel SAM (8.1.0) > Adafruit Feather M4 Express
HARDWARE: SAMD51J19A 120MHz, 192KB RAM, 512KB Flash
DEBUG: Current (atmel-ice) External (atmel-ice, jlink)
PACKAGES:
 - framework-arduino-samd-adafruit @ 1.7.10
 - framework-cmsis @ 2.50400.181126 (5.4.0)
 - framework-cmsis-atmel @ 1.2.2
 - toolchain-gccarmnoneeabi @ 1.90301.200702 (9.3.1)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 11 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Linking .pio\build\adafruit_feather_m4\firmware.elf
d:/docs/pjs/platformio/platformiocoredir/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: .pio\build\adafruit_feather_m4\src\main.cpp.o:(.bss.ui32Paolo+0x0): multiple definition of `ui32Paolo'; .pio\build\adafruit_feather_m4\src\HowTo.cpp.o:(.bss.ui32Paolo+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\adafruit_feather_m4\firmware.elf] Error 1
=============================================================================== [FAILED] Took 4.89 seconds ===============================================================================

This is the example
this is main.cpp

#include <Arduino.h>
#include <HowTo.h>
void setup() {
  // put your setup code here, to run once:
  SetupPaolo(25);
  ui32Paolo =0;
}
void loop() {
  // put your main code here, to run repeatedly:
  ui32Paolo ++;
  if (ui32Paolo>100) ui32Paolo =0;
  delay(100);
}

This is HowTo.cpp

#include <Arduino.h>
#include <HowTo.h>
void SetupPaolo(uint32_t ui32Loc){
ui32Paolo = ui32Loc;
}

This is HowTo.h

#include <Arduino.h>
#ifndef _HOWTOH_
#define _HOWTOH_
uint32_t ui32Paolo =0;
void SetupPaolo(uint32_t ui32Loc);
#endif

Thanks a lot for anybody that can explain me to parameterize the compiler or to understand a mistake of mine

No. You have to do extern uint32_t ui32Paolo; here and move uint32_t ui32Paolo = 0; into HowTo.cpp. If your global variable creation is in a header file and it’s included in multiple .cpp files, those .cpp files will each want to create that global variable, leading to multiple definition errors. In the header, you just have to declare that a certain variable variable of a certain type and name exist, but not define it. This is what extern does.

This is a really common beginner C++ mistake when trying to split code accross multiple files. For reference material, see

1 Like

Hello,

Thanks for your answer, I understood your suggestion.

Let met say that to avoid multiple instances I used the directive
#ifndef HOWTOH
#define -HOWTOH_

coding …

#endif

I’m used to avoid multiple instances using the directive, may be I’m wrong. How do you think about ?

Anyway thanks for your quick answer.

Best Regards

The include guard protects against multiple inclusion of the same header file by one .cpp file.

It does not protect against multiple .cpp files, each being their own compilation unit resulting in a .o object file, to include a header file.

So yes, each .cpp file including your header will include it at most one time thanks to the header guard. But that’s still multiple .cpp files, that thanks to the conent of the header file try to define a global variable in their .cpp file. And so multiple instances of the same global variable is trying to be created by multiple .cpp files.

So the header include guard provides 0 protection against that.

Hello,

Thanks for the answer,
I tried with your suggestion with extern directive but the error still present, so I tried with with static directive then the error disappears.

Please could you help me to understand better ?

I very appreciate your answer you are helping me

Best Regards
Thanks

This is not a solution!! You’ve introduced a bug now where each .cpp file themselves have a file-static variable which is not shared accross files. So the ui32Paolo variable in main.cpp can be of one value, and in HowTo.cpp of a completely different value.

Please show your updated code in full.

Hello All
using extern directive the error still present
using static directive all works fine

I hope that my informations are useful.
Thanks a lot for your attention.

This is the code
main.cpp
#include <Arduino.h>
#include <HowTo.h>

void setup() {
// put your setup code here, to run once:
SetupPaolo(25);
ui32Paolo =0;
}

void loop() {
// put your main code here, to run repeatedly:
ui32Paolo ++;
if (ui32Paolo>100) ui32Paolo =0;
delay(100);
}

this is HowTo.cpp
#include <Arduino.h>
#include <HowTo.h>

u_int32_t uint32Paolo =0 ;

void SetupPaolo(uint32_t ui32Loc){
ui32Paolo = ui32Loc;
}

This is HowTo.h
#include <Arduino.h>
#ifndef HOWTOH
#define HOWTOH

extern uint32_t ui32Paolo;
void SetupPaolo(uint32_t ui32Loc);
#endif

The compiler error (I guess the linker)
Processing adafruit_feather_m4 (platform: atmelsam; board: adafruit_feather_m4; framework: arduino)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via -v, --verbose option
CONFIGURATION: Redirecting...
PLATFORM: Atmel SAM (8.1.0) > Adafruit Feather M4 Express
HARDWARE: SAMD51J19A 120MHz, 192KB RAM, 512KB Flash
DEBUG: Current (atmel-ice) External (atmel-ice, jlink)
PACKAGES:

  • framework-arduino-samd-adafruit @ 1.7.10
  • framework-cmsis @ 2.50400.181126 (5.4.0)
  • framework-cmsis-atmel @ 1.2.2
  • toolchain-gccarmnoneeabi @ 1.90301.200702 (9.3.1)
    LDF: Library Dependency Finder → Library Dependency Finder (LDF) — PlatformIO latest documentation
    LDF Modes: Finder ~ chain, Compatibility ~ soft
    Found 11 compatible libraries
    Scanning dependencies…
    No dependencies
    Building in release mode
    Compiling .pio\build\adafruit_feather_m4\src\HowTo.cpp.o
    Linking .pio\build\adafruit_feather_m4\firmware.elf
    d:/docs/pjs/platformio/platformiocoredir/packages/toolchain-gccarmnoneeabi/bin/…/lib/gcc/arm-none-eabi/9.3.1/…/…/…/…/arm-none-eabi/bin/ld.exe: .pio\build\adafruit_feather_m4\src\HowTo.cpp.o: in function SetupPaolo(unsigned long)': HowTo.cpp:(.text._Z10SetupPaolom+0x8): undefined reference to ui32Paolo’
    d:/docs/pjs/platformio/platformiocoredir/packages/toolchain-gccarmnoneeabi/bin/…/lib/gcc/arm-none-eabi/9.3.1/…/…/…/…/arm-none-eabi/bin/ld.exe: .pio\build\adafruit_feather_m4\src\main.cpp.o: in function setup': main.cpp:(.text.setup+0x10): undefined reference to ui32Paolo’
    d:/docs/pjs/platformio/platformiocoredir/packages/toolchain-gccarmnoneeabi/bin/…/lib/gcc/arm-none-eabi/9.3.1/…/…/…/…/arm-none-eabi/bin/ld.exe: .pio\build\adafruit_feather_m4\src\main.cpp.o: in function loop': main.cpp:(.text.loop+0x14): undefined reference to ui32Paolo’
    collect2.exe: error: ld returned 1 exit status
    *** [.pio\build\adafruit_feather_m4\firmware.elf] Error 1

Hello

Here the Git repository

Regards
Paolo

With your code, as it is above, I get this error:

src/HowTo.cpp:4:1: error: 'u_int32_t' does not name a type; did you mean 'uint32_t'?

which makes sense. Changing u_int32_t to uint32_t in HowTo.cpp and recompiling results in these errors:

/tmp/cc4B9ct2.ltrans0.ltrans.o: In function `main':
<artificial>:(.text.startup+0x88): undefined reference to `ui32Paolo'
<artificial>:(.text.startup+0x8c): undefined reference to `ui32Paolo'
<artificial>:(.text.startup+0x90): undefined reference to `ui32Paolo'
<artificial>:(.text.startup+0x94): undefined reference to `ui32Paolo'
<artificial>:(.text.startup+0x9c): undefined reference to `ui32Paolo'
/tmp/cc4B9ct2.ltrans0.ltrans.o:<artificial>:(.text.startup+0xa0): more undefined references to `ui32Paolo' follow

This is because you have named the variable uint32Paolo in HowTo.cpp. Fixing that error, results in a clean compile.

So, in summary:

  • Fix u_int32_t in HowTo.cpp.
  • Fix uint32Paolo also in HowTo.cpp.

And you will get a clean compile. That solves current compilation errors, however, there might be some outstanding runtime errors to come, as in it doesn’t do what I think it should! :wink:

HTH

Cheers,
Norm.

Hello,
Sorry for the late feedback.
I corrected the syntax error, effectively the code works well.

Many thanks for the suggestions.

Please could you suggest me an effective link where to study & understand better the extern directive ?

Thanks a lot
:smile:

Well, I did a quick search for a good intoduction to extern and after looking at a few results, I wasn’t too happy. So, here’s my introduction. :wink:

Imagine you have a header file, call it header.h, and two source files, a.cpp and b.cpp, both of which #include "header.h".

If header.h contains only function declarations, such as int add(int a, int b); then all is well. Both a.cpp and b.cpp will compile, and the code in each can happily call the functions declared in the header file.

Now, if header.h contains something like int fred = 42;, then we have a problem.

a.cpp and header.h make up one compilation unit.

b.cpp and header.h make up another compilation unit.

Each compilation unit sees the declaration of fred and reserves some storage for an integer variable, and stores 42 in that storage location. The compiler will not throw up an error as each is a separate compilation unit.

The linker, on the other hand, will complain. It sees two variables called fred when it tries to link all compilation units into an executable. So, the “compilation” fails at the link stage with a duplicate definition error…

The solution is to declare fred in the header as extern int fred; which tells the compiler that “any source file which includes this header is able to see an int variable called fred.” Unfortunately, neither a.cpp or b.cpp reserve any storage for fred so the linker now complains about undefined variable fred.

We need to declare storage for fred, so, in either a.cpp or b.cpp – pick only one, it doesn’t matter which – and add int fred = 42; either before or after the #include "header.h" line. Now the compile and link will work.

The fact that fred is declared as an int and also declared extern in the same compilation unit doesn’t cause sn error.

Hope this helps.

Cheers,
Norm.

1 Like