How does IDE2.x.x deal with includes?

I have some conditional includes in my sketch:

#ifdef __AVR_ATmega328P__
    #include "src/Pinout_328P.h"
#elif __AVR_ATmega328PB__
    #include "src/Pinout_328PB.h"
#endif

Suppose I pick the UNO R3 board from Boards Manager. I would expect this to select the header included in the first condition but ignore the one in the second condition. Instead, it would appear that both are being compiled, which results in errors relating to resources that are only available for the board type relating to the file being included for the second condition (e.g. DDRE register).

Why is the compiler trying to compile both sets of .h/.cpp files?

I can probably prevent that by adding further defines and guards in both .h and .cpp files but I would have thought the conditional #ifdef as per the above would have been sufficient to include and compile ONLY the files selected by the conditional statement?

Just to add, that even if I comment out the two #include statements, the compiler is still trying to compile the files. It doesn't matter where the files are located within the sketch directory either. The only solution I have found so far to avoid a clash, is to comment out the includes and remove the files completely from the Sketch directory - or else use additional #define ... #ifdef ... guards.

I think this should be:

#ifdef __AVR_ATmega328P__
    #include "src/Pinout_328P.h"
#elif defined(__AVR_ATmega328PB__)
    #include "src/Pinout_328PB.h"
#endif

#ifdef is a shortform of #if defined(...) . But there is no such shortform for #elif.

That's a good observation and makes things look more consistent so I have added the keyword defined and put the term in brackets as shown but that has not changed the behavior of the compiler unfortunately.

What is your IDE version? Could you show your boards.txt file from the AVR hardware section?

Problem might be that both header files are in the src directory. Try putting each in its own subdirectory in src (along with any associated files).

IDE version is 2.3.6.

boards.txt (39.3 KB)

Aside from the #elif issue that has already been clarified, if a *.h still compiles even if you remove your #include statements, clearly some other module does just that.
Since I don't have your entire sketch I can't say which one, anyway you just need to examine all the *.h files you include and see who is the "culprit" and try to correct it from there (if it's possible).

This example illustrates the problem. It has no directory structure, just one flat project directory. Doesn't matter if you comment out one or the other or both of the defines, you still get the same errors due to the clash of function names, showing that both are being compiled regardless. If I comment out both, then you do get the expected 'func was not declared in this scope' errors:

INO:

#define INCLUDE1

//#define INCLUDE2


#ifdef INCLUDE1
  #include "include1.h"
#elif defined(INCLUDE2)
  #include "include2.h"
#endif


// The setup function runs once when you press reset or power the board
void setup() {

  Serial.begin(115200);

  delay(500);

  func1();
  func2();

  #ifdef INCLUDE2
    funcX();
  #endif

  Serial.flush();

}

// The loop function runs continuously
void loop() {

}

Include1.h:

#ifndef INCLUDE1_H
#define INCLUDE1_H

#include <Arduino.h>

void func1();
void func2();


#endif

Inlcude1.cpp:

#include "include1.h"

void func1(){
  Serial.println(F("This is include 1 function 1"));
}

void func2(){
  Serial.println(F("This is include 1 function 2"));
}

Include2.h:

#ifndef INCLUDE2_H
#define INCLUDE2_H

#include <Arduino.h>

void func1();
void func2();
void funcX();

#endif

Include2.cpp:

#include "include2.h"

void func1(){
  Serial.println(F("This is include 2 function 1"));
}

void func2(){
  Serial.println(F("This is include 2 function 2"));
}


void funcX(){
  Serial.println(F("This is include 2 function X"));
}

as @david_2018 said, every file in the src subdirectory will be processed automatically during Arduino build process.

I tried putting the files in separate directories as suggested, but that did not resolve the problem.`

I just tried the same example in OnlineGDB.com, but replacing occurrences of Serial.println() with std::cout and got the same result, which rules out the IDE. It seems you are correct. Its just the way that the compiler deals with project files. It processes all files present regardless of whether they are #include(ed) or not, so I guess additional guards will be necessary after all.

Your problem is not the compiler but the linker. Your ino file compiles ok, and so do both .cpp files. But if the linker wants to put it all together, it complains about the different functions with the same name in the two .cpp files. It has nothing to do with the #ifdef in the .ino file. Its simply the two .cpp files having different functions with the same name.

1 Like

I get that and yes its the linker not the compiler complaining. I stand corrected. However, I was expecting to take one file or the other out of the picture by commenting out or conditionally excluding it (by not including it), but that is evidently not how it works. It seems whether included or not, all files get processed by compiler - and linker - if they are in the project directory, regardless of whether they are #included or not, which is useful to be aware of.

I guess that does answer my question, even if the answer is not quite what I had expected, so thank you for the comments.

But that happens ONLY in the .ino file. When the compiler compiles your .cpp files it knows nothing about that, nor does the linker when it links everything together.

You need to check the board defines also in your .cpp files to compile only the board relevant functions.

You need to put the architecture-specific tests around all the code in your .cpp files also:

#ifdef __AVR_ATmega328P__

// ATmega328P - specific code goes here

#endif

Yes, that is the only other options and I have done that but the linker now complains about undefined references to functions inside the conditional statements which is where this all started.

`Difficulties with project files in hierarchical structure - #3 by b707

Having spent all day on this I am back to square one....

The easiest thing is to just leave things as they were. I had hoped to make this a little more structured and easier to work with. Didn't think it would end up being quite this complicated.

Well, obviously you can't attempt to call a function which does not exist due to conditional compilation. The call itself must be conditionally compiled.

Yes, agreed, but the functions do exist and should be visible to the linker. They are selected depending on the board platform being used, and there will always be a board platform selected so at least one group of functions will always exist. The calls always match the entry functions regardless of which platform is used.

The problem I currently have is that although the pin definitions in the .h file are being read (there were no errors relating to them), the linker complains about the functions in the corresponding .cpp file not existing, but how can that be, since the contents of the header file matches the condition and have been read? How can it work fine for one instance of a conditional compilation but fail for another instance of the same condition? The condition is identical in both files.

Post a Minimal Reproduceable Example (MRE) that demonstrates the problem. Also, post a diagram of your current directory structure.

Its almost identical to the example above in #9 except with the conditional clauses added into the .h and .cpp files.

Directory structure:

> Include-Test
  Include-Test.ino#
  > src
    > include1
      include1.cpp
      include1.h
    > include2
      include2.cpp
      include2.h

INO file:

#define INCLUDE1

//#define INCLUDE2


#ifdef INCLUDE1
  #include "src/include1/include1.h"
#elif defined(INCLUDE2)
  #include "src/include2/include2.h"
#endif


// The setup function runs once when you press reset or power the board
void setup() {

  Serial.begin(115200);

  delay(500);

  func1();
  func2();

  #ifdef INCLUDE2
    funcX();
  #endif

  Serial.flush();

}

// The loop function runs continuously
void loop() {

}

Include1.h:

#ifndef INCLUDE1_H
#define INCLUDE1_H

#include <Arduino.h>

#ifdef INCLUDE1

void func1();
void func2();

#endif  // INCLUDE1

#endif

Include1.cpp:

#include "include1.h"

#ifdef INCLUDE1

void func1(){
  Serial.println(F("This is include 1 function 1"));
}

void func2(){
  Serial.println(F("This is include 1 function 2"));
}

#endif

Include2.h:

#ifndef INCLUDE2_H
#define INCLUDE2_H

#include <Arduino.h>

#ifdef INCLUDE2

void func1();
void func2();
void funcX();

#endif  // INCLUDE2

#endif

Include2.cpp:

#include "include2.h"

#ifdef INCLUDE2

void func1(){
  Serial.println(F("This is include 2 function 1"));
}

void func2(){
  Serial.println(F("This is include 2 function 2"));
}


void funcX(){
  Serial.println(F("This is include 2 function X"));
}

#endif  // INCLUDE2

BTW, I have tried placing all files into the root sketch directory and obviously changing the #inlcude path,, but same result, so the problem does not appear to be being caused by the directory structure.