0

This question deals with fonts. Well, actually, singular include files with constants that form bitmap fonts or images for LCD or OLED displays, such as this one: https://github.com/ThingPulse/esp8266-oled-ssd1306/blob/master/src/OLEDDisplayFonts.h. A strangely hard to research (but common) problem occurs when anyone attempts to include one of these font files in multiple files in an application.

Even with an include-guard present, reuse of this .h file will cause strange linker errors about redefinition of a variable. It turns out that any variable actually declared in a .h file will cause it to be created multiple times in the program, and then when the different files depending on the font.h file are linked, the variables collide. An example of it would be here: https://stackoverflow.com/a/4936294/11381501, or in my very own question some time ago here: How to create large progmem arrays and not annoy the linker

The issue is, I've seen some libraries that avoid this error (such as https://github.com/ThingPulse/esp8266-oled-ssd1306/blob/master/src/OLEDDisplay.h#L64) despite having variables declared in .h files. I've even included this OLEDDisplay.h file (indirectly via the main library file for my screen) in multiple of my own .h files in one project and it hasn't caused linker errors. What causes this globally-scoped variable in a .h file to not produce linker errors when my own code usually does?

5
  • Include guards only protect against multiple inclusion within the same translation unit. Variables declared in headers like that should be static but really should be just declared in the header as extern and defined in their own separate TU.
    – Majenko
    Commented Apr 9, 2022 at 8:51
  • This is how I do fonts in my display library: github.com/CariadDisplayLibrary/WhiteRabbit/tree/master/src
    – Majenko
    Commented Apr 9, 2022 at 9:08
  • @Majenko, there is an alternative to static vs extern declaration (with definition elsewhere) in C++17 onwards, where inline may be applied to variables following a one-definition-rule scarcely different (if different at all) from inline specified functions.
    – timemage
    Commented Apr 9, 2022 at 15:58
  • @timemage There are far far too many "alternatives" in C++ nowadays. It's become a horrible tangled mess of "features"...
    – Majenko
    Commented Apr 9, 2022 at 16:22
  • @Majenko Well, it's a solution that involves the insertion of a single keyword. I thought that would have been something helpful to know.
    – timemage
    Commented Apr 10, 2022 at 14:00

1 Answer 1

1

The esp8266-oled-ssd1306 has it wrong, but with examples of the library it is saved by optimization. Other files where the font gets included don't use it so it is optimized away. Otherwise it would be duplicated.

OLEDDisplayFonts.h is included in OLEDDisplay.h which is included in OLEDDisplay.cpp and in SSD1306Wire.h which is included in the sketch. This creates two instances of the variables defined in OLEDDisplayFonts.h. But the instance in OLEDDisplay.cpp is optimized away, because that file doesn't use fonts. So for the linker there is only one instance of the variable.

relevant part from .map file for the SSD1306UiDemo.ino example

 .irom.text.OLEDDisplayFonts.h.857.11
                0x000000004023b7fd     0x25ab ./SSD1306UiDemo.ino.cpp.o
 .irom.text.OLEDDisplayFonts.h.434.10
                0x000000004023dda8     0x13b9 ./SSD1306UiDemo.ino.cpp.o
 .irom.text.OLEDDisplayFonts.h.10.9
                0x000000004023f161      0xaab ./SSD1306UiDemo.ino.cpp.o
 .irom.text.OLEDDisplayFonts.h.10.9
                0x000000004023fc0c      0xaab ./libraries/esp8266-oled-ssd1306/src/OLEDDisplay.cpp.o
 .irom.text.OLEDDisplayFonts.h.10.9
                0x00000000402406c7      0xaab ./libraries/esp8266-oled-ssd1306/src/OLEDDisplayUi.cpp.o

The right way is to put variables in a cpp or c file and if they should be visible from other file, then use extern keyword in that file (usually over an included .h file) to declare that the variable exists.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.