Search code examples
c++compiler-errorsc-preprocessorcompiler-warningspreprocessor

Interesting behaviour of preprocessor-command #define (regarding compilability)


In an PlatformIO project I have a wrapper around this RFM69 library. The header file (named radio.h) contains the following:

#pragma once
#include <RFM69.h>

//struct for wireless data transmission
typedef struct {
    ...
} Payload;

class RFMRadio {
  private:
    RFM69* radio;

  public:
    RFMRadio();
    ~RFMRadio();
    void Init();
    void Send(int aDeviceID, float aValue);
    void Sleep();

    bool receiveDone(Payload& msg);
    void listenModeStart();
    void listenModeEnd();
};

In the project the listenMode of the RFM69 library is used (although it is buggy as the library itself states, unfortunately my superviser still wants to use those functions).

Natively this mode is not activated (line 174-184 of RFM69.h from the library linked above):

//Native hardware ListenMode is experimental
//It was determined to be buggy and unreliable, see https://lowpowerlab.com/forum/low-power-techniques/ultra-low-power-listening-mode-for-battery-nodes/msg20261/#msg20261
//uncomment to try ListenMode, adds ~1K to compiled size
//FYI - 10bit addressing is not supported in ListenMode
// #define RF69_LISTENMODE_ENABLE

#if defined(RF69_LISTENMODE_ENABLE)
  // By default, receive for 256uS in listen mode and idle for ~1s
  #define  DEFAULT_LISTEN_RX_US 256
  #define  DEFAULT_LISTEN_IDLE_US 1000000
#endif

What I wanted to do is to write #define RF69_LISTENMODE_ENABLE in my radio.h file right before the #include <RFM69.h> statement like this:

#pragma once
#define RF69_LISTENMODE_ENABLE
#include <RFM69.h>
...

If I do this the code will not be compiled instead I get a lot of warnings like type 'struct RFM69' violates the C++ One Definition Rule [-Wodr] (Full errorlog at the bottom). However when I simply uncomment the // #define RF69_LISTENMODE_ENABLE line from the library code above the build runs without any problems. So with respect to the place where RF69_LISTENMODE_ENABLE is defined it is either possible to compile the code or not. I double checked if the RFM69-library is included elsewhere in the project an this is not the case.

I wonder what causes this behaviour?

Full error log:

.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:186:7: warning: type 'struct RFM69' violates the C++ One Definition Rule [-Wodr]
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: a different type is defined in another translation unit
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:315:10: note: the first difference of corresponding definitions is field '_isHighSpeed'
     bool _isHighSpeed;
          ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: a type with different number of fields is defined in another translation unit
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:212:10: warning: 'ACKRequested' violates the C++ One Definition Rule  [-Wodr]
     bool ACKRequested();
          ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:355:6: note: implicit this pointer type mismatch
 bool RFM69::ACKRequested() {
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: type 'struct RFM69' itself violates the C++ One Definition Rule
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:186:7: note: the incompatible type is defined here
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:355:6: note: 'ACKRequested' was previously declared here
 bool RFM69::ACKRequested() {
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:355:6: note: code may be misoptimized unless -fno-strict-aliasing is used
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:235:10: warning: 'writeReg' violates the C++ One Definition Rule  [-Wodr]
     void writeReg(uint8_t addr, uint8_t val);
          ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:546:6: note: implicit this pointer type mismatch
 void RFM69::writeReg(uint8_t addr, uint8_t value)
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: type 'struct RFM69' itself violates the C++ One Definition Rule
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:186:7: note: the incompatible type is defined here
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:546:6: note: 'writeReg' was previously declared here
 void RFM69::writeReg(uint8_t addr, uint8_t value)
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:546:6: note: code may be misoptimized unless -fno-strict-aliasing is used
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:203:10: warning: 'initialize' violates the C++ One Definition Rule  [-Wodr]
     bool initialize(uint8_t freqBand, uint16_t ID, uint8_t networkID=1);
          ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:60:6: note: implicit this pointer type mismatch
 bool RFM69::initialize(uint8_t freqBand, uint16_t nodeID, uint8_t networkID)
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: type 'struct RFM69' itself violates the C++ One Definition Rule
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:186:7: note: the incompatible type is defined here
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:60:6: note: 'initialize' was previously declared here
 bool RFM69::initialize(uint8_t freqBand, uint16_t nodeID, uint8_t networkID)
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:60:6: note: code may be misoptimized unless -fno-strict-aliasing is used
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:216:10: warning: 'encrypt' violates the C++ One Definition Rule  [-Wodr]
     void encrypt(const char* key);
          ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:503:6: note: implicit this pointer type mismatch
 void RFM69::encrypt(const char* key) {
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: type 'struct RFM69' itself violates the C++ One Definition Rule
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:186:7: note: the incompatible type is defined here
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:503:6: note: 'encrypt' was previously declared here
 void RFM69::encrypt(const char* key) {
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:503:6: note: code may be misoptimized unless -fno-strict-aliasing is used
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:227:10: warning: 'sleep' violates the C++ One Definition Rule  [-Wodr]
     void sleep();
          ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:215:6: note: implicit this pointer type mismatch
 void RFM69::sleep() {
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: type 'struct RFM69' itself violates the C++ One Definition Rule
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:186:7: note: the incompatible type is defined here
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:215:6: note: 'sleep' was previously declared here
 void RFM69::sleep() {
      ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:215:6: note: code may be misoptimized unless -fno-strict-aliasing is used
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:201:5: warning: '__comp_ctor ' violates the C++ One Definition Rule  [-Wodr]
     RFM69(uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW_HCW=false, SPIClass *spi=nullptr);
     ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:42:1: note: implicit this pointer type mismatch
 RFM69::RFM69(uint8_t slaveSelectPin, uint8_t interruptPin, bool isRFM69HW_HCW, SPIClass *spi) {
 ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.h:186:7: note: type 'struct RFM69' itself violates the C++ One Definition Rule
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69/RFM69.h:186:7: note: the incompatible type is defined here
 class RFM69 {
       ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:42:1: note: '__comp_ctor ' was previously declared here
 RFM69::RFM69(uint8_t slaveSelectPin, uint8_t interruptPin, bool isRFM69HW_HCW, SPIClass *spi) {
 ^
.pio\libdeps\pro8MHzatmega328\RFM69\RFM69.cpp:42:1: note: code may be misoptimized unless -fno-strict-aliasing is used
C:\Users\LennArt\AppData\Local\Temp\ccb4dJLO.ltrans0.ltrans.o: In function `SensorTempHum::handle(Payload*)':
<artificial>:(.text+0x127e): undefined reference to `RFM69::listenModeEnd()'
C:\Users\LennArt\AppData\Local\Temp\ccb4dJLO.ltrans0.ltrans.o: In function `main':
<artificial>:(.text.startup+0x74e): undefined reference to `RFM69::listenModeSetDurations(unsigned long&, unsigned long&)'
<artificial>:(.text.startup+0x75a): undefined reference to `RFM69::listenModeStart()'
<artificial>:(.text.startup+0x792): undefined reference to `RFM69::listenModeEnd()'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\pro8MHzatmega328\firmware.elf] Error 1

Solution

  • I double checked if the RFM69-library is included elsewhere in the project an this is not the case.

    What about the library itself? You have to compile both your project and the library with the exactly the same version of RFM69.h.

    It looks like you added RF69_LISTENMODE_ENABLE into your project but are using the library without it, meaning RFM69.cpp saw different header file with incompatible types.

    Of course in better world the library should not rely on these macros tricks.

    Add the macro definition to the Makefile, CMakeLists or whatever build system you are using for your project and the library.