1

I have a strange problem when compiling a C++ code using a makefile. The code first compiles perfectly. Then I change one function argument to "const". If I then compile, I will receive the error message when the code tries to use the function in which I changed the argument to const. This can be resolved by removing all .o files and then compiling again, but I am curious as to what causes this issue in the first place. My files are:

MyClass.h

class MyClass {
public:
void fun(double*const c);
};

MyClass.cpp

#include "MyClass.h"
void MyClass::fun(double *const c){
};

Main.cpp

#include "MyClass.h"
int main(int argc,char* argv[]) {
    MyClass foo;
    double *bar=new double[2];
    foo.fun(bar);
};

Makefile

all: main

main: Main.o MyClass.o 
    g++ -o a.out Main.o MyClass.o


Main.o: Main.cpp
    g++ -c Main.cpp


MyClass.o: MyClass.cpp
    g++ -c MyClass.cpp

If I now first run make, everything works. But then I change the signature of fun to fun(const double *const c), I receive the error message

Main.cpp:(.text+0x3b): undefined reference to `MyClass::fun(double*)'
collect2: error: ld returned 1 exit status
Makefile:6: recipe for target 'main' failed
make: *** [main] Error 1

However, if I remove all the .o files and then run make again, it compiles.

Jonathan Lindgren
  • 1,192
  • 3
  • 14
  • 31

2 Answers2

3

The rule

main.o: Main.cpp

says thay the main.o object file only depend on the Main.cpp source file. But it actually depends on another file: The MyClass.h header file.

There's also an error in the capitalization of the name of the target.

The above two problems means that when you change the header file MyClass.h to update the function signature, the Main.o object file will not be recreated and still reference the old function.

So the rule should be

Main.o: Main.cpp MyClass.h

That addition will cause the Main.o object file to be recompiled when you change the header file.

This change should also be done for the MyClass.o target.


Also note that the main target uses MyClass.o as a dependency, but then you use the MyClass.cpp source file when linking, instead of the object file.

The name of the target should also be the name of the generated file (i.e. a.out in your case).

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Thank you, the main.o vs Main.o and MyClass.cpp were typos though, sorry about that. Including MyClass.h as a dependency however indeed solves the problem! But a question then, why is it enough to add MyClass.h as a dependency for Main.cpp, and not also MyClass.cpp? – Jonathan Lindgren Sep 14 '17 at 08:19
  • 1
    @JonathanLindgren Because `make` already recognizes that you changed `MyClass.cpp` and it needs to be recompiled. – user0042 Sep 14 '17 at 08:22
  • Suggest using `-MD` or similar gcc switch to autogenerate these dependencies – M.M Sep 14 '17 at 08:29
0

The problem is, that your Makefile is broken: It ignores the fact that a .o file does not only depend on the corresponding .cpp file, but also on the .h files it includes. A correct Makefile rule must include all dependencies, including the directly, or even indirectly included .h files.

Since you Makefile is incomplete, make did not recompile the calling site of your function when its definition changed, so the stale object still references the non-const function. Due to C++ name-mangling, the linker caught it in the act, the error would have gone unnoticed in C!

To fix this situation for good, I recommend spending an hour or two reading this article on automatic dependency generation, and implementing some of the solutions it offers into your Makefile. Once you have done this, you will likely just copy-cat your existing solution into the Makefiles of future projects, and otherwise forget about the problem.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106