Search code examples
c++classprogram-entry-point

Include in main and class body returns multiple definition error


I am trying to compile a main function which includes a class header. The main function as well as the class cxx file use a helper function called "SayHello". However, if I include the helper file "Hello.h" in the main and the class cxx file I get a multiple definition error. I put minimal examples below. Can you help me?

Cheers, Andreas

Main.cxx:

#include "Class.h"
#include "Hello.h"

int main(int argc, char **argv){
    SayHello();
    return 0;
}

Class.h

#ifndef CLASS_H
#define CLASS_H

class Class {
public:
    Class();
    ~Class();
};

#endif

Class.cxx

#include "Class.h"
// #include "Hello.h" //with this it breaks!

Class::Class(){
    // SayHello(); //with this it breaks!
}

Hello.h

#ifndef HELLO_C
#define HELLO_C

#include <iostream>

void SayHello(){
  std::cout<<"hello!"<<std::endl;
}

#endif

My makefile:

# Compiler
CXX           = $(shell root-config --cxx)

# Compiler flags
ROOTCFLAGS   := $(shell root-config --cflags)
ROOTLIBS     := $(shell root-config --libs)
CFLAGS        = -Wall ${ROOTCFLAGS} ${INCLUDE_PATH}
LFLAGS        = -O3 ${ROOTLIBS} -lHistPainter

# Targets
EXE = Main
OBJS = Main.o Class.o

# Processing
# -------------------------------------
all: ${OBJS} ${EXE}
    @echo "Done"

${EXE}: ${OBJS}
    @echo "Making executable $(notdir $@)"
    ${CXX} ${CFLAGS} ${OBJS} ${LFLAGS} -o $@

${EXE}.o: ./${EXE}.cxx
    @echo "Compiling $(notdir $<)"
    ${CXX} $(CFLAGS) -c $< -o $@ 

%.o: ./%.cxx ./%.h
    @echo "Compiling $(notdir $<)"
    ${CXX} $(CFLAGS) -c $< -o $@ 

clean:
    @echo "Cleaning"
    @rm -f ./Main
    @rm -f ./Main_cxx.so
    @rm -f ./Main_cxx.d
    @rm -f ./*.o
    @rm -f ./*ACLiC_dict_rdict.pcm

Solution

  • You put the definition of SayHello function in a header file. This is wrong.

    Since you included this header file in two different files, this definition is compiled two times, in two different files, and your linker finally see two definitions of the same function at the end.

    This is why your header file Hello.h should only contains a prototype of the function:

    #ifndef HELLO_C
    #define HELLO_C
    
    void SayHello();
    
    #endif
    

    While the definition should be in a separate Hello.cxx file:

    #include <iostream>
    #include "Hello.h"
    
    void SayHello(){
      std::cout<<"hello!"<<std::endl;
    }
    

    And of course, don't forget to add this new file to your Makefile:

    OBJS = Main.o Class.o Hello.o
    

    Update:

    As Rene said, you also can simply declare your function as inline by adding the inline keyword in the header. It will make your compiler copy the entire function content each time this function is called, instead of doing a function call.

    This is often used for very short functions, typically getters and setters functions. As your function is just a "Hello" print, it may not a bad practice to make it an inline function.

    But be careful: too many inlined function in your code shall slow down compilation, as well as increase includes dependency. For instance, if you inline SayHello function, any file that would include "Hello.h" shall include <iostream> as well.