Reputation: 1582
I am new to C/C++, I already know the basics and I am starting to learn more advanced concepts now.
Currently I am developing a project using C++ and it is quite a big one, so structuring my project will be better.
From what I have already seen, a good structure relies on, at least, to folders: /src
and /include
. All .cpp files should go in /src
folder and .hpp ones in /include
. Here the first doubt arose: how to include a header which is not in the same directory nor in a standard one? After searching about it I concluded that the best way is to pass the option -I../include
to the compiler.
I had three source files: main.cpp, GraphData.cpp and RandomGraphGenerator.cpp. I have read that it is best practice to have a header for each of these files, to separate declaration from definition. Now I also have two headers, but I am not getting how to compile all of these.
I am focusing making GraphData.cpp to be linked correctly to main.cpp, I commented everything that needs RandomGraphGenerator.cpp. This is my main file:
// main.cpp
#include <iostream>
#include <string>
#include <Snap.h>
#include <GraphData.hpp>
int main() {
std::string file_path;
char path[1024];
DataPoint **data_set;
int motif_size, num_null_models;
std::cin >> file_path;
std::cin >> motif_size;
std::cin >> num_null_models;
data_set = new DataPoint*[num_null_models];
strncpy(path, file_path.c_str(), sizeof(path));
path[sizeof(path) - 1] = 0;
//read input
PNGraph G = TSnap::LoadEdgeList<PNGraph>(path, 0, 1);
//enumerate and classify k-subgraphs in G
GD::extractData(&G, motif_size, data_set[0]);
//generate random graphs and enumerate and calssify k-subgraphs on them
for(int i=1; i<=num_null_models; i++) {
//GM::randomize(&G);
GD::extractData(&G, motif_size, data_set[i]);
}
//detect motifs
GD::discoverMotifs(data_set);
return 0;
}
My GraphData header:
#ifndef GRAPHDATA_HPP_INCLUDED
#define GRAPHDATA_HPP_INCLUDED
#include <Snap.h>
struct DataPoint;
namespace GD {
void extractData(PNGraph*, int, DataPoint*);
DataPoint* discoverMotifs(DataPoint**);
}
#endif // GRAPHDATA_HPP_INCLUDED
And my GraphData file:
#include <GraphData.hpp>
struct DataPoint {
int label;
int frequency = 0;
};
void GD::extractData(PNGraph* G, int k, DataPoint* data_array) {
//stuff
}
DataPoint* GD::discoverMotifs(DataPoint** data_set) {
//dummy code
DataPoint* dp;
dp = new DataPoint[2];
dp[0].label = 10;
dp[1].label = 99;
return dp;
}
I am trying to compile, under Ubuntu 14.10 with GCC 4.9.2, using:
g++ -o main main.cpp /usr/include/Snap-2.3/snap-core/Snap.o -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core
But it gives me an error of undefined referece:
main.cpp:(.text+0x179): undefined reference to `GD::extractData(TPt<TNGraph>*, int, DataPoint*)'
main.cpp:(.text+0x1b9): undefined reference to `GD::extractData(TPt<TNGraph>*, int, DataPoint*)'
main.cpp:(.text+0x1dd): undefined reference to `GD::discoverMotifs(DataPoint**)'
collect2: error: ld returned 1 exit status
I am a little bit lost with it, what am I missing?
Upvotes: 3
Views: 15233
Reputation: 1259
g++ by default invokes the linker (ld) as soon as it has finished compiling all the input files. There are many ways to produce the binary executable, but the two most common are:
g++
command. You would need to add GraphData.cpp
and main.cpp
to the same call to g++
.
This method ensures the linker has access to all the symbols to create the executable. -c
switch to g++
for each cpp file, then call g++ again and pass the .o
files instead of the .cpp
files.Either way, at some point, you have to give all the symbols to the linker at once so it can create the executable.
As Joachim explained, anything beyond a couple files becomes a huge pain, at which point learning a build system can be quite profitable. He mentions make, but I would also consider looking into cmake which has simpler syntax and is cross platform. It can even generate Makefiles
for you automatically.
Upvotes: 5
Reputation: 409472
[Note: This isn't an answer to the question, it's just an explanation on how to compile multiple files one by one]
You can compile the source files separately, but then you have to tell g++
to create object files, which you then link together.
Something like this:
$ g++ -Wall -g -c -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core main.cpp
$ g++ -Wall -g -c -I../include -I/usr/include/Snap-2.3/snap-core -I/usr/include/Snap-2.3/glib-core GraphData.cpp
$ g++ -g main.o GraphData.o /usr/include/Snap-2.3/snap-core/Snap.o -o main
The option -Wall
tells g++
to enable more warnings, this is good because warnings are signs of something that you might be doing wrong (like causing undefined behavior). The -g
options tells g++
to generate debug information, always good to have while developing in case you need to run the program in a debugger. Finally the -c
option is what tells g++
to generate an object file instead of an executable program.
Then you have a separate command that links the object files together to form the final executable.
If you get more than a couple of files this all becomes kind of a pain in the backside, and then you should learn about make
which is a program which can automate much of this for you. It will, for example, check time-stamps on the files to make sure you don't compile unmodified files.
Upvotes: 1