Including a file leads to compilation issues with libraries

I am currently working on a project using C++11 on an Arduino Nano 33 BLE. As per PlatformIO documentation, I have included different classes of mine by using the lib folder, which gives me the following file organisation:

├── Makefile
├── compile_commands.json
├── lib
│   ├── AlarmManager
│   │   └── src
│   │       ├── Alarm.h
│   │       ├── AlarmManager.cpp
│   │       ├── AlarmManager.h
│   │       ├── Test.cpp 
│   │       ├── Test.h       <-- used to demonstrate the issue later
│   │       ├── Time.cpp
│   │       ├── Time.h
│   │       └── Weekday.h
│   ├── BLE
│   │   └── src
│   │       ├── BLE.h
│   │       ├── CurrentLightService.cpp
│   │       ├── CurrentLightService.h
│   │       ├── CurrentTimeService.cpp
│   │       └── CurrentTimeService.h
│   ├── LightManager
│   │   └── src
│   │       ├── LightManager.cpp
│   │       └── LightManager.h
│   ├── Subscriber
│   │   └── src
│   │       ├── LightSubscriber.h
│   │       └── TimeSubscriber.h
│   └── TimeManager
│       └── src
│           ├── TimeManager.cpp
│           └── TimeManager.h
├── platformio.ini
├── src
│   └── main.cpp
└── test
    ├─...

I am trying to add a new class, AlarmManager, which is in lib/AlarmManager/AlarmManager.h. This alarm manager depends on two other classes: LightManager and TimeManager. Both of them are included in other files, such as CurrentLightService or CurrentTimeService without any issue. However, when I include them in AlarmManager, I have compilation errors.

The errors do not concern my code, but the libraries I use. I have a lot of errors, which all look alike. Here are just a few of them:

/home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/cmsis/CMSIS_5/CMSIS/RTOS2/Include/cmsis_os2.h:207:3: error: conflicting declaration 'typedef enum osStatus_t osStatus_t'
 } osStatus_t;
   ^~~~~~~~~~
In file included from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/Kernel.h:28,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/rtos.h:28,
                 from lib/LightManager/src/LightManager.h:6,
                 from lib/AlarmManager/src/Test.h:4,
                 from lib/AlarmManager/src/Test.cpp:1:
/home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/mbed_rtos_types.h:59:3: note: previous declaration as 'typedef enum osStatus_t osStatus_t'
 } osStatus_t;
   ^~~~~~~~~~
*** [.pio/build/nano33ble/libea4/AlarmManager/Test.cpp.o] Error 1
/home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/cmsis/CMSIS_5/CMSIS/RTOS2/Include/cmsis_os2.h:205:32: error: 'osErrorISR' conflicts with a previous declaration
   osErrorISR                = -6,         ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
                                ^
In file included from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/Kernel.h:28,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/rtos.h:28,
                 from lib/LightManager/src/LightManager.h:6,
                 from lib/AlarmManager/src/Test.h:4,
                 from lib/AlarmManager/src/Test.cpp:1:
/home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/mbed_rtos_types.h:57:5: note: previous declaration 'osStatus_t osErrorISR'
     osErrorISR              = -6,         ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
     ^~~~~~~~~~
In file included from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/platform/include/platform/SingletonPtr.h:26,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/connectivity/netsocket/include/netsocket/SocketStats.h:21,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/connectivity/netsocket/include/netsocket/InternetSocket.h:31,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/connectivity/netsocket/include/netsocket/UDPSocket.h:24,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/connectivity/netsocket/include/netsocket/nsapi.h:43,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/mbed.h:28,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed.h:14,
                 from lib/TimeManager/src/TimeManager.h:5,
                 from lib/AlarmManager/src/Test.h:5,
                 from lib/AlarmManager/src/Test.cpp:1:

/home/furnost/.platformio/packages/framework-arduino-mbed/variants/ARDUINO_NANO33BLE/pinmode_arduino.h:38:16: error: 'PullDown' conflicts with a previous declaration
In file included from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h:45,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/TARGET_ARDUINO_NANO33BLE/device.h:37,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/hal/include/hal/ticker_api.h:25,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/drivers/include/drivers/TimerEvent.h:20,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/platform/include/platform/internal/SysTimer.h:22,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/platform/include/platform/internal/mbed_os_timer.h:21,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/Kernel.h:31,
                 from /home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/rtos/include/rtos/rtos.h:28,
                 from lib/LightManager/src/LightManager.h:6,
                 from lib/AlarmManager/src/Test.h:4,
                 from lib/AlarmManager/src/Test.cpp:1:
/home/furnost/.platformio/packages/framework-arduino-mbed/cores/arduino/mbed/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/TARGET_ARDUINO_NANO33BLE/PinNames.h:237:5: note: previous declaration 'PinMode PullDown'
     PullDown = 1,
     ^~~~~~~~

I decided to make a simple Test.h to showcase the problem:

#ifndef TEST_H
#define TEST_H

#include <LightManager.h>
#include <TimeManager.h>

class Test {
   public:
    Test(LightManager& lightManager, TimeManager& timeManager);

    LightManager& lightManager;
    TimeManager& timeManager;
};

#endif

Here is the corresponding Test.cpp:

#include <Test.h>

Test::Test(LightManager& lightManager, TimeManager& timeManager)
    : lightManager(lightManager), timeManager(timeManager){};

Here is the LightManager.h:

#ifndef LIGHT_MANAGER_H
#define LIGHT_MANAGER_H

#include <LightSubscriber.h>
#include <mbed_events.h>
#include <rtos.h>

#include <set>

class LightManager : public LightPublisher {
   public:
   // rest is ommited for brevity

LightManager.cpp (also just the includes):

#include <Arduino.h>
#include <LightManager.h>
#include <math.h>

TimeManager.h:

#ifndef TIME_MANAGER_H
#define TIME_MANAGER_H

#include <TimeSubscriber.h>
#include <mbed.h>
#include <mbed_events.h>
#include <rtos.h>

#include <set>

class TimeManager : public TimePublisher {
   public:
  // ommited for brevity

TimeManager.cpp:

#include "TimeManager.h"
#include <Arduino.h>

Finally, here is the include section of my main.cpp:

#include <Arduino.h>
#include <ArduinoBLE.h>
#include <BLE.h>
#include <CurrentLightService.h>
#include <CurrentTimeService.h>
#include <LightManager.h>
#include <Test.h> // <-- If I don't include this, everything works fine
#include <TimeManager.h>
#include <mbed_events.h>
#include <platform/Callback.h>
#include <rtos.h>

const int zcPin = 2;
const int pwmPin = 3;
...

Removing #include <Test.h> makes everything compile and work correctly. If I do not inlcude LightManager and/or TimeManager in Test.h, everything also works.

I really don’t understand what is happening, if anyone has an idea or wishes to see something in particular, please let me know.

Strange errors like this can occur if:

  • #ifdef / #endif aren’t properly matched
  • several header files with the same name exist (on Windows and macOS capitalization of file name is ignored – so Test.h is the same as test.h)
  • there is no line feed after #endif

First check that there is a line feed after #endif in Test.h.

Thanks @manuelbl.

There were line feed after the #endif. But I finally found out what was wrong. It was simply the order of the includes.

Including TimeManager before LightManager makes everything work. The only difference in includes between the two is mbed.h, and including mbed.h in LightManager allows me to include TimeManager and LightManager in any order.

I suspect that Arduino.h and/or mbed.h do some weird initialization…

Thanks anyways !

Time.h is a very dangerous name to give a file. It conflicts with the C standard library’s time.h, which probably uses the same _TIME_H include guard. On Windows due to the nature of case-insensitive filenames, time.h == Time.h and so mbed-os code doing a #include <time.h> might end up including your library’s Time.h, screwing with it a lot.