staticrgbarduino-idearduino-esp8266arduino-c++

Why does defining a static class object in C++ cause a "multiple definition" error?


I am currently making a program in Arduino IDE to make animations with LEDs. My concept is that there is a base Animation class that holds the static RGBLed object that allows me to light up LEDs in the draw() method. All the children of Animation are supposed to inherit this object and the method. This is my Animation.cpp:

#include <RGBLed.h>

const int GPIN = D0, RPIN = D1, BPIN = D2; //RGB MOSFET pins


class Animation{
protected:
  int R = 0, G = 0, B = 0;
  static RGBLed myLED;

public:
  void update(int currentTime){}

  void draw(){
    myLED.setColor(R, G, B);
  }
};
RGBLed Animation::myLED(RPIN, GPIN, BPIN, RGBLed::COMMON_CATHODE);


class Animation1: public Animation{
private:
  int timer = 0;
  int anim_time = 1000;
  
public:
  Animation1(){
    timer = millis();
  }

  void update(int currentTime){
    //do some RGB magic
    }
  }
};


class RGBSwipe: public Animation{
private:
  float H = 0.0, S = 1.0, V = 1.0;
  int timer = 0;
  int delay_time = 10;

public:
  RGBSwipe(){
    timer = millis();
  }

  void update(int currentTime){
    //do some HSV magic
    }
  }

private:
  void HSVtoRGB(){
    //Conversion happens here
  }
};

I get this error:

(USER)/appdata/local/arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/3.1.0-gcc10.3-e5f9fec/bin/../lib/gcc/xtensa-lx106-elf/10.3.0/../../../../xtensa-lx106-elf/bin/ld.exe: (USER)\AppData\Local\Temp\arduino-sketch-3EC4D14CA84EC5BABE4C744AA5D32B6B\sketch\FFT_analyzer.ino.cpp.o:(.bss._ZN9Animation5myLEDE+0x0): multiple definition of `_ZN9Animation5myLEDE'; (USER)\AppData\Local\Temp\arduino-sketch-3EC4D14CA84EC5BABE4C744AA5D32B6B\sketch\Animation.cpp.o:(.bss._ZN9Animation5myLEDE+0x0): first defined here collect2.exe: error: ld returned 1 exit status

exit status 1

Compilation error: exit status 1

I tried re-locating the definition but nothing seems to work. Any Ideas?


Solution

  • In your main.cpp, you have a #include "Animation.cpp".

    Don't do that.

    Never #include an implementation .cpp file, only header files .h or .hpp. There are exceptions to that rule for advanced uses, like creation of an amalgamation, but this is only for expert C++ programmers.

    All .cpp files shall be compiled separately. This is what is called a compilation unit. In your situation, the content of Animation.cpp was compiled twice, once for its own and once inside main.cpp. Ans the linker has two copies for each identifier.

    This is not a problem for the class declarations (they should indeed be declared in shared header files). This is not a problem neither for the function member definitions, since as they are placed inside the class block, are implicitly inline.

    It happens this is not a problem neither with GPIN, RPIN and BPIN variables. As they are declared const at global scope, they are implicitly static, meaning there is one distinct copy per compilation unit (usually compiled out by the optimizer however).

    But RGBLed Animation::myLED(RPIN, GPIN, BPIN, RGBLed::COMMON_CATHODE); is a definition, which must be defined once to follow the One Definition Rule (ODF).

    So move most of your code into Animation.h, and #include "Animation.h" into both main.cpp and Animation.cpp. The definition of Animation::myLED must be in Animation.cpp and not in header. Also, I suggest to move the code marked // do some magic out of the class declaration into the implementation .cpp file. Better reserve inline methods to small functions like getters and setters. Also, prefer constexpr to declare constant values instead of old const.