Makefile not working when changing function argument to const - c++

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.

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).

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.

Related

VSCode Makefile no longer creating executable, which fails when make is invoked

So I was practicing with a tutorial series on C++ projects for Linux. In order to create the makefile I did CTR+SHIFT+P to go into Palet, searched for make file, selected the correct option, and selected C++ project. In the tutorial, the person changed src in the make file to a static path ie: pwd. That worked. When he changed back to src and after moving the files into the src folder like he did, doing make clean, and then make, I get this:
[termg#term-ms7c02 HelloWorldnonVR]$ make
g++ -std=c++11 -Wall -o obj/list.o -c src/list.cpp
g++ -std=c++11 -Wall -o obj/main.o -c src/main.cpp
g++ -std=c++11 -Wall -o HelloWorld obj/list.o obj/main.o
/usr/bin/ld: obj/main.o: in function `List::List()':
main.cpp:(.text+0x0): multiple definition of `List::List()'; obj/list.o:list.cpp:(.text+0x0): first defined here
/usr/bin/ld: obj/main.o: in function `List::List()':
main.cpp:(.text+0x0): multiple definition of `List::List()'; obj/list.o:list.cpp:(.text+0x0): first defined here
/usr/bin/ld: obj/main.o: in function `List::~List()':
main.cpp:(.text+0x2c): multiple definition of `List::~List()'; obj/list.o:list.cpp:(.text+0x2c): first defined here
/usr/bin/ld: obj/main.o: in function `List::~List()':
main.cpp:(.text+0x2c): multiple definition of `List::~List()'; obj/list.o:list.cpp:(.text+0x2c): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:37: HelloWorld] Error 1
[termg#term-ms7c02 HelloWorldnonVR]$
There are three files. A class and its header, and then the main. list.h is in the include subfolder for src.
I currently cannot find any information about this problem, so any help would be appreciated. The tutorial had no issues with making or running the files. The VSCode extension is C/C++ Makefile Project. My system is Manjaro. I did do make clean and delete the makefile to start fresh, in case I hit the keyboard or something but same result persists. From what I am seeing, the issue is that there is no HelloWorld executable being created. HelloWorld is the appname in the template.
Narrowed the issue down to the header. Without constructors it works but with them it does not.
#include <iostream>
#include <vector>
using namespace std;
class List
{
private:
/* data */
protected:
public:
void print_menu(); //Prototype
void print_list();
void add_item();
void delete_item();
vector<string> list;
string name;
//constructor
List(/* args */);
//destructor
~List();
};
List::List(/* args */)
{
}
List::~List()
{
}
Any ideas on what is causing this?
The error message tells you exactly what the problem is, if you learn the compiler-ese to interpret it:
main.cpp:...: multiple definition of `List::List()'; obj/list.o:list.cpp:...: first defined here
Here it's saying you have defined the constructor twice: once in main.cpp and once in list.cpp.
And, as is the case 99.99999% of the time, the compiler (well in this case technically the linker) is correct. You have defined the constructor and destructor in the header file, and you've included the header file in both the main.cpp file and in the list.cpp file, so the constructor and destructor are defined in both, just as the error says.
You need to put the constructor and destructor in the list.cpp source file, not in the header file.
Alternatively you can put them inside the class itself, which makes them inline, and then you won't have this issue:
class List
{
private:
/* data */
protected:
public:
List(/* args */) {}
~List() {}
void print_menu(); //Prototype
Of course this only makes sense if they are small and simple enough to inline like this.

No multiple definition error despite including source file in another and compiling both

Let's say I have 3 files:
Test.hh
#ifndef TEST_HH_
#define TEST_HH_
class Test
{
int test() const;
};
#endif /* TEST_HH_ */
Test.cc:
#include "Test.hh"
int Test::test() const
{
return 0;
}
main.cc:
#include "Test.cc"
int main()
{
return 0;
}
It does not compile (rather does not link), I understand why, I defined Test::test() in multiple translation units (in main.cc that includes Test.cc and in Test.cc):
g++ -Wall -g -std=c++17 -c main.cc -o main.o
g++ -Wall -g -std=c++17 -c Test.cc -o Test.o
g++ -o bin main.o Test.o
Test.o: In function `Test::test()':
Test.cc:12: multiple definition of `Test::test()'
main.o:Test.cc:12: first defined here
collect2: error: ld returned 1 exit status
Edit: This is NOT my issue, my issue is that with a seemingly identical situation, in a bigger project, the previous example produces a binary (i.e. compiles and links) when, as far as I understand it, it shouldn't. I will now describe the real case with a bit more details and how the problem suddenly arised when previously it was working fine (when it shouldn't have).
--
I am currently working on a large project (~2500 files), and while trying to use the "Test" class above, I ended up having a lot of "multiple definitions" errors at link time. To translate it to our example, it's like I had another class doing this:
OtherClass.hh
#include "Test.hh" // including or using forward declaration led to the same results
//class Test; forward declaration
class OtherClass
{
// Some stuff, whatever
};
I ended up finding that Test.cc was included in another source file (main.cc in my very simplified exemple though it was in another "someClass.cc" in my actual project). After including the header instead of the source, it compiled again. What's more surprising is that other classes had been using Test.hh the same way until then without any problems.
Since I was really surprised, I ended up doing a grep on all my files and found that another 2 source files had included other sources files as well.
WhateverClass.cc
#include "Test2.cc"
YetAnotherClass.cc
#include "Test3.cc"
All those files are compiled and contain function definitions yet the linker does not complain. I tried doing a compilation from scratch and it still worked.
So my question is: Why does this compile even though some source files include others and all of them are compiled ? And why did it suddenly stop working even though I just included the header of one of those source files just like other classes had been doing ? Is there a kind of "undefined behavior" for cases like this ?
If it is of any help, my project is using CMake. I tried compiling with ninja or Make with the same results.
I had a similar question before. So, here's what I learned- never include source file. Only include header files. Your error comes from including a source (.cc) file in your main. You should include Test.hh instead. Declare all your classes and functions in header, include that header to all the source files where the definitions and the calls are.

Deleting *.cpp file after compiling with wrong flag order?

I really need your help on this one!
I had a problem with my makefile. The error was very common:
makefile:11: recipe for target 'exec' failed
My makefile looks like this:
CC = g++
PY = python
FLAGS = -std=c++11 -O3
all: main exec data
main: main.cpp
$(CC) $(FLAGS) -o $# $<
exec: main
time ./$<
data: plot.py main
$(PY) $<
As far as im concerned there is no mistake but I still got the error, maybe main.cpp was not compiled?
Anyway I then tried (out of curiosity):
g++ -std=c++11 -O3 -o main.cpp main
And then I got this error:
main: In function `_start':
(.text+0x1360): multiple definition of `_start'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o:(.text+0x0): first defined here
main: In function `_fini':
(.fini+0x0): multiple definition of `_fini'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crti.o:(.fini+0x0): first defined here
main:(.rodata+0x0): multiple definition of `_IO_stdin_used'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o:(.rodata.cst4+0x0): first defined here
main: In function `data_start':
(.data+0x0): multiple definition of `__data_start'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o:(.data+0x0): first defined here
main:(.rodata+0x8): multiple definition of `__dso_handle'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/crtbegin.o:(.rodata+0x0): first defined here
main: In function `_init':
(.init+0x0): multiple definition of `_init'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crti.o:(.init+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/crtend.o:(.tm_clone_table+0x0): multiple definition of `__TMC_END__'
main:(.data+0x8): first defined here
/usr/bin/ld: error in main(.eh_frame); no .eh_frame_hdr table will be created.
collect2: error: ld returned 1 exit status
And now my main.cpp is gone. What the actual.. How can I recover it? I have an older version of it because I'm using git, but its rather unfinished and I really need this version.
The error was very common
That's the make error. What did it show before that? Did it run the compiler at all? Was there a compile error? What was it?
I addressed your proximate problem below, but the original issue is the compile error. You're fixating on the fact that a build target failed, rather than on understanding why it failed.
Builds fail all the time: very commonly because there's a mistake in the code, and very rarely because of an error in a previously-working makefile. As a consequence, you usually want to worry about understanding and fixing the compiler error, before you start changing your makefile or running the compiler manually.
And now my main.cpp is gone. What the actual..
Well, in
g++ -std=c++11 -O3 -o main.cpp main
the option -o filename tells g++ to use filename for output. So, it opened main.cpp for output and clobbered the contents.
You meant
g++ -std=c++11 -O3 -o main main.cpp
It's an easy mistake to make, which is why we have build systems to do this stuff for us. And backups. And version control.
In future, you can just type make main to select a single target, and make should tell you what it's doing.
How can I recover it?
From your editor, or version control, or backup, or filesystem-level snapshotting if you have a fancy SAN or, in extremis, from memory.
Nothing teaches good source control and backup habits like having to rewrite something from scratch.
there is no main.cpp file anymore. why exactly is that?
When the build failed, the incomplete output file was deleted.
Consider, for reference, how make works. You have a target called main, so it checks whether a file called main exists.
if main exists, it looks at the dependencies, and sees if any of those files are newer then main
if it thinks main should be (re-)generated (either it doesn't exist or is older than a dependency), it runs the rule you gave it
Now if g++ left an empty (or incomplete) version of main lying around after a failed compilation, how would make know to re-generate it the next time you built?
It's essential that the output file be deleted when compilation fails, because otherwise make wouldn't work correctly. You'd also have a directory full of empty or corrupt partly-compiled executables and object files, which doesn't sound like such a great idea.
In other news, your exec target doesn't create a file called exec. This should be a .PHONY rule. So, probably, should data.
The problem is:
g++ -std=c++11 -O3 -o main.cpp main
Where you specify that the output should be placed in main.cpp. You try to compile main and store the result in main.cpp. Since the compile failed, the output file is removed after compilation stopped. So no main.cpp.
It's simple: Just save the file again in your editor.
To do so, press C-x C-w and type the filename again.

Undefined Reference To Member function

I'm trying to solve a compiling problem regarding outside defined constructor.
Here's 2 of the classes that have this problem:
//Username.h
#ifndef USERNAME_H
#define USERNAME_H
#include <string>
using namespace std;
class Username{
private:
string Name;
public:
Username(string = "");
string getName() const;
void setName(string);
};
#endif
...
//Username.cpp
#include "Username.h"
Username::Username(string n):Name(n){}
string Username::getName() const{return Name;}
void Username::setName(string n){Name = n;}
...
//User.h
#ifndef USER_H
#define USER_H
#include <string>
#include "Username.h"
using namespace std;
class User{
protected:
Username* UserUsername;
public:
User(string s);
virtual ~User();
Username* getUsername() const;
void setUsername(Username*);
};
#endif
...
//User.cpp
#include "User.h"
User::User(string s):UserUsername(new Username(s)){}
User::~User(){}
Username* User::getUsername() const{return UserUsername;}
void User::setUsername(Username* u){UserUsername=u;}
int main(){return 0;}
If I compile using "g++ User.cpp" I get this error:
/tmp/ccEmWmfl.o: In function `User::User(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
User.cpp:(.text+0x3e): undefined reference to `Username::Username(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
collect2: ld returned 1 exit status
If I use "g++ User.cpp Username.cpp -o main" or if I use inline constructors/destructors I get no error.
These classes are very easy, but I have tons more to compile that require more than one class.
I'm new at compiling in Ubuntu shell with g++, so please, can someone help me to understand?
g++ User.cpp
This compiles User.cpp and attempts to create an executable from it (ie the linker is invoked). Since User.cpp has a symbol not fully defined in User.cpp (the Username constructor here:
User::User(string s):UserUsername(new Username(s)){}
the symbol is expected to be defined at the link stage. Linking is done by combining all the generated object file output of all the cpps you generated and piecing together the missing symbols. Here you're not telling g++ about where to find the full definition of the Username constructor other than the cpp with main, so its failing.
This, however:
g++ User.cpp Username.cpp -o main
Tells the linker where to find full Username definitions (in the object file generated by compiling Username.cpp). So linking can succeed in filling in the missing pieces by using whats defined in Username.cpp to match up identifiers not defined in User.cpp.
You may think -- "well I've told the compiler about it by including the header file, it should know!". What you've done is declared the symbol. You've made a promise that something will eventually be defined, either during compilation of that one cpp or by later linking it with another object file generated by compiling another cpp. g++ needs to know where you intend to pull all your definitions from so it can build a final executable correctly.
You already answered your question, if you don't add Username.cpp at the compilation process User can't knwo it.
You can use partial compilation, with the -c flag:
g++ -c User.cpp
This produces a User.o file.
You do this partial compilation thing on each of your files
g++ -c Username.cpp
And then you link all the object files (the *.o files) together:
g++ User.o Username.o -o main
Usually, you'd use some build system to automate this process. With that you'd also get other advantages, like not skipping the recompilation of files that didn't change since the last compilation.
This can also happen, if you are trying to call a member function of a template class from the source file other than the implementation file of the template class. For example: you have clas ABC with ABC.h and ABC.cpp (implementation file).
As long as your calls to generic objects lies in ABC.cpp, all works well. However, if you try to include ABC.h in another class say BCD and call any member function of object ABC, you will get the Undefined Reference To Member function error.
Solution: Create a single header file for your template classes including the implementation to avoid this error which is misleading.
User.cpp add:
#include "Username.h"
It's not really a C++ problem, but more a GCC issue. Normally, you'd build a larger program with Make. You then have a makefile rule that builds all .cpp files in a directory and links hem together. An alternative is that your makefile specifically states which .cpp files should be compiled together.
You have to pass all of your implementation files to the g++ executable. The first compilation attempt fails because you're only compiling User.cpp into an object file and linking it into an executable object called main.
In the second attempt you correctly pass both necessary implementation files to the g++ executable. In this case, User.cpp and Username.cpp are compiled into object files and linked together to form the main executable. In this case, the implementation of the Username::Username() constructor is present.

c++ undefined reference to <a function>

HI,
I have some questions about .h files and .cpp files in c++/linux(ubuntu).
It is possible to compile a .h file using g++ or you can just compile a .cpp file that includes the .h file?
From a .h file and it's .cpp file (.cpp where i include some code to the methods i've defines in .h file) I create a .so file using the command:
g++-fPIC -shared my_code.cpp -o my_code.so`
In the test.cpp I include the .h file and using dlopen i create a handler over the .so file. Why do I have the following error:
undefined reference to bool `Class::method(std::string)` `collect2: ld returned 1 exit status
If I say virtual bool method... in the .h file there is no error when I compile test.cpp. Can someone explain what am I doing wrong? The thing is that i have a template. With templates I cannot use virtual..so..i have this undefined error and i don't know how to resolve it. THX
EDIT:
When i compile the my_code.cpp I have the errors:
/usr/bin/ld: .usr/lib/debug/usr/lib/crt1.o relocation 0 has invalid symbol index 12 (same with index 13,2,14...22 ).
But when i create the .so file there is no error . I use:
g++ test.coo -ldl -o test
for the test.cpp compilation.
Ad 1: It is possible to compile .h file (you can explicitly override the language detection), but you don't want to do it. The .h file is intended to be included and will not compile to anything useful on it's own.
Ad 2: You have to link against the library you created by passing the -lmy_code (but note that for that to work you have to create it as libmy_code.so) along with appropriate -L flag (directory where you placed libmy_code.so) to the linker. Like this:
g++ test.cpp -L. -lmy_code -ldl -o test
But you also have to change the first command to:
g++ -fPIC -shared my_code.cpp -o libmy_code.so
^^^
libraries *must* have `lib` prefix on unix systems.
and this assumes both are done in the same directory—if not, you have to adjust the -L option to point to the directory where libmy_code.so is. Also you have to place libmy_code.so somewhere where the dynamic linker can find it. Either by installing it or by setting environment variable LD_LIBRARY_PATH to appropriate directory. Alternatively you can compile by using
g++ test.cpp my_code.so -ldl -o test
This does not force the lib prefix and it creates an "rpath" entry in the binary so it will find the library in the original place.
This all assumes you want to use it as regular library in which case you don't want to use dlopen. dlopen is for opening libraries as plugins at runtime and than they can only be accessed by fetching pointers to symbols using dlsym(), but if you want to access the library normally, you have to link against it so the linker can resolve the symbols.
If instead you wanted to use dlopen, you must not include my_code.h in test.cpp and must not use anything it defines except by getting the symbols with dlsym. And since this is C++, it in turn requires you understand the symbol mangling scheme, because dlsym will not do this for you.
Generally there is no need to compiling a .h file, it simply generates a huge file with .gch extension I guess.
The error you are getting is a link time. While creating the .so file, you do not actually link the code. So all undefined symbol are assumed to be present at some place. When you link it, the linker will find for those symbols. So, you should compile/link all the .cpp file together. The error will go away.
Also, For templates, the definition of the code must always be visible. So wherever you write the templated function/variable definition, include that file everywhere.
Edit:
You can have virtual method with template classes; but you can not have virtual template methods.
template<typename T>
class A {
virtual void foo(int); // ok
};
class A {
template<typename T>
virtual void foo(T); // illegal
};