Error: redefinition of class, despite using include guards

Viewed 707

I'm a javascript/typescript developer but newbee to Arduino/c++

I have a class (see h and cpp below) and have this compiler error:

DotMatrix.cpp:13:1: error: redefinition of 'DotMatrix::DotMatrix(uint8_t, MD_MAX72XX::moduleType_t, uint8_t, uint8_t, uint8_t)'
 DotMatrix::DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
 ^
In file included from sketch/DotMatrix.cpp:5:0:
DotMatix.h:10:3: error: 'DotMatrix::DotMatrix(uint8_t, MD_MAX72XX::moduleType_t, uint8_t, uint8_t, uint8_t)' previously defined here
   DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
   ^
exit status 1

and I don't understand why...

Here are the files:

DotMatix.h:

#ifndef DotMatrix_h
#define DotMatrix_h

#include <MD_MAX72xx.h> 
#include <MD_Parola.h> 

class DotMatrix
{
  public:
    DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
    : _parola(hardwareType, csPin, maxDevices)
    {};

    void setup();
  
  private:
    MD_Parola _parola;
};

#endif

DotMatix.cpp:

#ifndef DotMatrix_cpp
#define DotMatrix_cpp

//#include <stdint.h>
#include "DotMatix.h"
#include "Arduino.h"

#include <MD_Parola.h>  // Parola library to scroll and display text on the display (needs MD_MAX72xx library)  https://github.com/MajicDesigns/MD_Parola
#include <MD_MAX72xx.h> // Library to control the Maxim MAX7219 chip on the dot matrix module   https://github.com/MajicDesigns/MD_MAX72XX

const int FRAME_DELAY = 25;

DotMatrix::DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
: _parola(hardwareType, csPin, maxDevices)
{
  //MD_Parola(MD_MAX72XX::moduleType_t mod, uint8_t csPin, uint8_t numDevices = (uint8_t)'\001')
  _parola = MD_Parola(hardwareType, csPin, maxDevices);  
}

void DotMatrix::setup()
{
  this->_parola.begin();
  _parola.displayClear();
  _parola.displaySuspend(false);
  byte i = 3;                                            //EEPROM.read(0);
  _parola.setIntensity(i);                               // Values from 0 to 15
  _parola.setTextEffect(PA_SCROLL_LEFT, PA_SCROLL_LEFT); //in and out effect
  _parola.displayScroll("Hallokes ...", PA_LEFT, PA_SCROLL_LEFT, FRAME_DELAY);
}

#endif

For reference, this is (a snippet of) the ino file calling the class:

#include "DotMatix.h"

// Define the number of 8x8 dot matrix devices and the hardware SPI interface
#define MAX_DEVICES 4
#define HARDWARE_TYPE MD_MAX72XX::DR1CR0RR0_HW

#define CLK_PIN   14 //D5
#define DATA_PIN  13 //D7
#define CS_PIN    2  //D4


DotMatrix dotMatrix(MAX_DEVICES, HARDWARE_TYPE, CLK_PIN, DATA_PIN, CS_PIN);

void setup() {
  dotMatrix.setup();
}

void loop() {
}

I'm using the VS Code IDE with the Arduino Extension.

4 Answers
void func(); // function declaration
void func() { blablabla; } // function definition.

You may see the simple difference between a definition and declaration. One has {, the other does not. A (global) function may be defined only once in a codebase, not twice. Otherwise your compiler will not know which function to choose - the first one? The second one?

You did:

// in DotMatix.h:
DotMatrix(blablaa)
{}; // yes this is definition

and also did:

// in DotMatrix.cpp
DotMatrix::DotMatrix(ublabla, who cares)
{ // also definition

The compiler sees two definitions of the same function and exits with an error.

Include guards are typically used to protect against including the same file twice. Your multiple definitions are in multiple files. Also include guard in a source file are not needed - source files are not meant to be included, so they will always be processed once.

You shall change this line of DotMatrix.h:

  DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
    : _parola(hardwareType, csPin, maxDevices)
    {};

It should be:

   DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
        : _parola(hardwareType, csPin, maxDevices);

If not, you are implementing the constructor directly in header file (constructor doing nothing). The implementation being "{}" (nothing to be done) Then when compiler sees the other implementation (the correct one) in .cpp file, it says it has already been defined.

You defined the constructor twice. The first one definition of DotMatrix is within the class definition itself in the header DotMatix.h:

class DotMatrix
{
  public:
    DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
    : _parola(hardwareType, csPin, maxDevices)
    {};
    //...

Note that the null statement ; after the constructor definition is redundant.

This definition defines an inline function.

Pay attention to that the constructor has unused parameters.

The second definition of the constructor DotMatrix is within the module DotMatix.cpp:

DotMatrix::DotMatrix(uint8_t maxDevices, MD_MAX72XX::moduleType_t hardwareType, uint8_t clkPin, uint8_t dataPin, uint8_t csPin) 
: _parola(hardwareType, csPin, maxDevices)
{
  //MD_Parola(MD_MAX72XX::moduleType_t mod, uint8_t csPin, uint8_t numDevices = (uint8_t)'\001')
  _parola = MD_Parola(hardwareType, csPin, maxDevices);  
}

Moreover this definition contains a redundant assignment of the data member _parola within the body of the constructor.

Remove entirely this definition of the constructor in the module DotMatix.cpp.

And these guard directives

#ifndef DotMatrix_cpp
#define DotMatrix_cpp

do not make great sense because modules are not designed to be included in other modules.

Thanks for all the input! I've deleted the constructor in the cpp file and all is working now. Also the include guard in the cpp file was indeed a stupid thing to do!

Coming from js / typescript, this whole header file and weird initialiser list syntax is still creating shortcuts in my head ;-)

About that initialiser list: is this the only way to declare a variable of a class with a constructor with parameters? This is looking so weird to me ...

Related