Preprocessor problem

Hi!
I'm facing a strange problem...
On my main ino file, on first line, i have a definition:

#define MYDEF

#include "Ethernet.h"

Then i include the ethernet library, modified in this way:

#ifndef	W5100_H_INCLUDED
#define	W5100_H_INCLUDED

#define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0)
#pragma message "14"
...

#if defined(MYDEF)
#pragma message "4"
#undef SPI_ETHERNET_SETTINGS
#define SPI_ETHERNET_SETTINGS SPISettings(4000000, MSBFIRST, SPI_MODE0)
#endif


...
#endif

And this is the output of the compiler:

C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src/utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src/utility/w5100.h:54:17: note: #pragma message: 4
 #pragma message "4"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\EthernetClient.cpp:24:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\Dhcp.cpp:7:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\Dns.cpp:8:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\Ethernet.cpp:23:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\EthernetICMP.h:32:0,
                 from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\EthernetICMP.cpp:42:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\EthernetServer.cpp:23:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\EthernetUdp.cpp:32:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\socket.cpp:23:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility/w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"
                 ^
In file included from C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility\w5100.cpp:13:0:
C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src\utility\w5100.h:23:17: note: #pragma message: 14
 #pragma message "14"

Why the first time the preprocess do what is expected, then simply skip the #if condition??
And also: if the first time it set the

W5100_H_INCLUDED

Why the subsequent times it ignores the

#ifndef	W5100_H_INCLUDED

and compute the #pragma?

The end result is that it uses the wrong SPI_ETHERNET_SETTINGS...

I'm simply trying to reduce the SPI speed for the transactions with the ethernet module.

Thanks for the help!

This w5100.h file is located in same folder/directory as your .ino file ?
That is, your .ino file located under: C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src/utility/ ?

The reason for this is that the .ino file of your sketch is a separate "translation unit" from the .cpp files of the Ethernet library.

When you compile your sketch, the .cpp files of the Ethernet library are also compiled. The #include directive is essentially the equivalent of just copy pasting the entire contents of the .h file where the #include directive is. So the contents of Ethernet.h is compiled in multiple translation units. The #defines from one translation unit have no effect on another translation unit. So MYDEF is defined when Ethernet.h is compiled as part of your sketch file, but it is not defined when Ethernet.h is compiled as part of Ethernet.cpp. This is why the conditional compilation is done differently for each translation unit.

This is why people get all excited about not being able to add -D flags to the compiler commands. The -D
flag allows you to define macros for all translation units, which is not possible when using a #define directive in your sketch (unless the sketch is the only translation unit).

6v6gt:
This w5100.h file is located in same folder/directory as your .ino file ?
That is, your .ino file located under: C:\Users\xxx\Documents\Arduino\libraries\Ethernet\src/utility/ ?

No, different folders

pert:
The reason for this is that the .ino file of your sketch is a separate "translation unit" from the .cpp files of the Ethernet library.

When you compile your sketch, the .cpp files of the Ethernet library are also compiled. The #include directive is essentially the equivalent of just copy pasting the entire contents of the .h file where the #include directive is. So the contents of Ethernet.h is compiled in multiple translation units. The #defines from one translation unit have no effect on another translation unit. So MYDEF is defined when Ethernet.h is compiled as part of your sketch file, but it is not defined when Ethernet.h is compiled as part of Ethernet.cpp. This is why the conditional compilation is done differently for each translation unit.

This is why people get all excited about not being able to add -D flags to the compiler commands. The -D
flag allows you to define macros for all translation units, which is not possible when using a #define directive in your sketch (unless the sketch is the only translation unit).

That explain all. the first occurrence is evaluated as originated from my ino, but the other libraries that call the same include get a whole new set of definitions.
So, what's the purpouse of #ifndef in the beginning of every H files?
Can this impact the code optimizations, as the same function are included over and over?
Thanks for the explaination!

masterx81:
So, what’s the purpouse of #ifndef in the beginning of every H files?

The are known as “include guards”. Their purpose is to prevent the header file content from being included multiple times in the same translation unit, which would cause a compilation error. But they allow the inclusion once in each translation unit, which is exactly what you want them to do.

masterx81:
the same function are included over and over?

Header files traditionally only contain declarations. There are some exceptions of inline function definitions, and these can have an impact on optimization.

Probably not appropriate for the Ethernet library, but related to this subject you might be interested in the approach used in the Encoder library:

// If you define ENCODER_DO_NOT_USE_INTERRUPTS *before* including
// Encoder, the library will never use interrupts.  This is mainly
// useful to reduce the size of the library when you are using it
// with pins that do not support interrupts.  Without interrupts,
// your program must call the read() function rapidly, or risk
// missing changes in position.
#define ENCODER_DO_NOT_USE_INTERRUPTS
#include <Encoder.h>

This might seem odd at first, since you just discovered that attempting to control conditional compilation of libraries via #define directives in the sketch doesn’t work so well.

The reason it’s possible with the Encoder library is because the author put all the relevant source code in Encoder.h:

So the sketch’s translation unit contains the library source code that is being controlled by the macro defined in the sketch.

So i have no way to force a compilation condition on another module, that will be always evaluated.
And i suspect that also the already present conditional definitions in the ethernet library aren't evaluated.
IE:

#if defined(ARDUINO_ARCH_ARC32)
#undef SPI_ETHERNET_SETTINGS
#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
#endif

would be nice a way to declare a "global define" that is always evaluated... (as the -D flags seem to do)

masterx81:
i suspect that also the already present conditional definitions in the ethernet library aren't evaluated.
IE:

#if defined(ARDUINO_ARCH_ARC32)

#undef SPI_ETHERNET_SETTINGS
#define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
#endif

Board identification macros like ARDUINO_ARCH_ARC32 are defined via a -D flag in the compilation commands, so it is defined in every translation unit.

masterx81:
would be nice a way to declare a "global define" that is always evaluated... (as the -D flags seem to do)

There are ways to do it, but only via the Arduino IDE's command line interface or by modifying the configuration of the Arduino board plaform. You can see prior discussion about that by following the links off this proposal:

But you can't define macros globally from a sketch, and this is by design. The Arduino developers are concerned that if this capability was provided, library authors would start incorporating it into the standard user interfaces. As we can see from the discussion here, the preprocessor is very confusing. I only know what I know because I ran into this same perplexing behavior you reported and spent quite some time tearing my hair out before I learned about this concept of "translation units". While it's an important thing to learn eventually, it's not something a beginner should be subjected to. For them, something like this is much more intuitive:

SomeLibrary.begin(someConfigurationValue);

Ok, perfect all clear… Can’t be explained better! I’m a .net asp/vb developer, quite new to the C++/arduino world, so this problems were totally counterintuitive to me… It’s important to know this limitation.
In any case i’m more concerned about the code optimization (in small equipped flash/ram devices as the mcu’s): when the compiler process more times the same prototype function definition / variables (every time that it encounter the same include in different “translation units”), he know that actually they are the same things? Because i’ve noticed that with exactly the same code, playing moving functions in different includes, the flash usage was changing - while the compiled code was doing exacly the same.

One more thing, how i know what -D flag the compiler is actually using?

masterx81:
In any case i'm more concerned about the code optimization (in small equipped flash/ram devices as the mcu's): when the compiler process more times the same prototype function definition / variables (every time that it encounter the same include in different "translation units"), he know that actually they are the same things?

I can tell you that the compiler is very good at optimizing the code. But certainly the choices you make when programming can play a role too. As for getting in to the specifics of your question, I'm not really the best one for that. But there are some super C++ wizards here on the forum I'm sure can step in and give you the lowdown.

masterx81:
how i know what -D flag the compiler is actually using?

Do this:

  • File > Preferences
  • Check the box next to "Show verbose output during: Compilation".
  • Click the "OK" button.
  • Compile a sketch.

After compilation finishes, scroll up the black console window at the bottom of the Arduino IDE window. There you will see all the compilation commands the Arduino IDE ran to compile your sketch. These commands contain the -D flags.

There is an exception to what I said above for certain boards. For example, if you're compiling for the Arduino Nano 33 BLE, you'll see something like this in the compilation commands:

"@C:\\Users\\per\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\mbed\\1.3.2\\variants\\ARDUINO_NANO33BLE/defines.txt"

This @ tells the compiler to add the contents of the file at that path as flags. You can see the contents of the file here:

This particular one contains a large collection of -D flags that would have made the compilation commands too long if they were used in the traditional manner directly in the command line.

Whoa!
Thanks for all, you have put some light on some unclear (and not so well documented) aspects of the arduino environment!