This page contains a guide to create projects with more than one cpp file.
We have a class, CalculatorClass, which we want to use in one cpp filen and define in another, so we put it into a header file, say calc.h, which could look like:
#ifndef CALC_H_INC #define CALC_H_INC #include <string> class CalculatorClass { public: double Add(const std::string &aS1, const std::string &aS2); }; #endifThe #ifndef/#define/#endif is a "include-guard", which insures that the header can be included more than once in the same cpp file without problems.
We implements the Add function in calc.cpp:
#include <string> #include <sstream> #include "calc.h" template <typename T> bool FromString(T &aValue, const std::string &aStr) { std::stringstream ss(aStr); return ss >> aValue; } double CalculatorClass::Add(const std::string &aS1, const std::string &aS2) { double N1, N2; FromString(N1, aS1); FromString(N2, aS2); return N1 + N2; }We include our calc.h file to know the CalculatorClass.
Then we create a main.cpp which is a small test-application:
#include <iostream> #include <string> #include "calc.h" int main() { std::string S1, S2; std::cout << "Enter first number: "; std::getline(std::cin, S1); std::cout << "Enter second number: "; std::getline(std::cin, S2); CalculatorClass Calculator; double Result = Calculator.Add(S1, S2); std::cout << S1 << " + " << S2 << " Is: " << Result << std::endl; }Now to compile it all to a program use one of these, depending of your compiler:
bcc32 calc.cpp main.cpp g++ calc.cpp main.cpp -o calc.exe cl calc.cpp main.cpp dmc calc.cpp main.cppThe result will in all four cases be calc.exe
These are the things to put into header-files:
- #define
- types and class's
- prototypes for functions
- external variable defenitions
- template functions
- inline functions
But not:
- functions
- variables
When your projects grow it will take time to compile everything whenever you make a small change in one file.
Makefiles are a handy way to control the compiler and ensure that only what needs to be compiled is.
We reuse the code from above.
A makefile is file with rules of how to build a application, the makefile is read by a make program.
Most compilers has there own make program, so the syntax used differs a bit.
The main building block in a makefile is:
target: dependencies commandNote that some make (gcc) programs has the strange request that the indention before command must be a tab character, not spaces.
A simple example could be:
main.exe: main.cpp g++ main.cpp -o main.exeHere main.exe is the target, if main.exe does not exist or is older than main.cpp (the only dependency) the command (g++ ...) will be executed.
If we have more than one file we could use:
calc.exe: main.cpp calc.cpp calc.h bcc32 calc.cpp main.cppThen, if any of the dependencies are changed, calc.exe will be build.
But if you have a project with hundreds of .cpp and .h files it is inconvenient to build everything if you only changed a bit in a single cpp-file. It is exactly here makefile show there strength, as they will know what to build, if we tell it.
The project could be build with these commands, if Digital Mars compiler is used:
dmc -c main.cpp dmc -c calc.cpp dmc -L/SI/DELEXECUTABLE main.obj calc.obj calc.exeThe first two lines will compile the .cpp file into a .obj file, because we used the -c switch.
The last line will link the two .obj files into the executeable.
Then, if we change main.cpp we just have to execute two of the tree lines; we don't have to compile calc.cpp.
If we put this into a makefile it will be:
calc.exe: main.obj calc.obj dmc -L/SI/DELEXECUTABLE main.obj calc.obj calc.exe main.obj: main.cpp dmc -c main.cpp calc.obj: calc.cpp dmc -c calc.cppHere main.obj depend on main.cpp, so if main.cpp is modified dmc -c main.cpp will be executed, and main.obj will be created. As calc.exe depends on main.obj the command to build calc.exe will also be executed.
Note that calc.exe is the first target in the file, this is the one which will be executed if no other is specified.
If you just want to compile main.cpp and don't want do link, you can run the command:
make -f makefile.mak main.objBut if you have lots of .cpp files, it will be boring to write a unique rule for each, but then you can use:
.cpp.obj: dmc -c $*.cppThis is a rule to create a .obj file from a .cpp file, with the same name, $* will expand to the basename of the cpp file. It is a implicit rule, it will be used if there is no normal (explicit) rule.
But what about the headerfile? We have to tell that main.cpp and calc.cpp includes calc.h, that is main.obj and calc.obj depends on calc.h, one way to do it:
main.obj: main.cpp calc.h dmc -c main.cppOr you can use this method:
main.obj: calc.h calc.obj: calc.hThat is, put the dependencies on seperate lines. This has the advantage that you can use tools to create these lines, or if you are using gcc/g++ let the compiler create the lines.
You have to use the latter if you use the implicit rule.
Note that if you are using Borland's make and there compiler it will figure out the dependencies on it own.
You can use variables in makefiles:
LinkFlags=-L/SI/DELEXECUTABLETo use a variable incluse it in $():
calc.exe: main.obj calc.obj dmc $(LinkFlags) main.obj calc.obj calc.exeOr to use it for everything:
#Makefile for Digital Mars CC=dmc Link=dmc CppFlags=-c ExeFile=calc.exe ObjectFiles=main.obj calc.obj LinkFlags=-L/SI/DELEXECUTABLE $(ExeFile): $(ObjectFiles) $(Link) $(LinkFlags) $(ObjectFiles) $(ExeFile) .cpp.obj: $(CC) $(CppFlags) $*.cpp main.obj: calc.h calc.obj: calc.hNow if you add a cpp file to the project you simply add the name of it's .obj file to the ObjectFiles variable.
Note that # is used to denote comments
With VisualC++ the makefile could look like this:
#Makefile for Visual C++ CC=cl Link=cl CppFlags=-c ExeFile=calc.exe ObjectFiles=main.obj calc.obj $(ExeFile): $(ObjectFiles) $(Link) $(LinkFlags) $(ObjectFiles) /Fe$(ExeFile) .cpp.obj: $(CC) $(CppFlags) $*.cpp main.obj: calc.h calc.obj: calc.hWith G++:
#Makefile for G++ CC=g++ Link=g++ CppFlags=-c ExeFile=calc.exe ObjectFiles=main.obj calc.obj $(ExeFile): $(ObjectFiles) $(Link) $(LinkFlags) $(ObjectFiles) -o $(ExeFile) %.obj: %.cpp g++ $(CppFlags) -o $@ $< main.obj: calc.h calc.obj: calc.hAnd finnaly BorlandC:
#Makefile for BorlandC CC=bcc32 Link=bcc32 CppFlags=-c ExeFile=calc.exe ObjectFiles=calc.obj main.obj $(ExeFile): $(ObjectFiles) $(Link) $(ObjectFiles) .cpp.obj: $(CC) $(CppFlags) $*.cpp main.obj: calc.h calc.obj: calc.hIf you name the makefile makefile.mak you can run it with:
make -f makefile.makOr if you are using Visual Compiler and its make:
nmake -f makefile.makIt is often handy to cleanup everything, to do that we can add a new target, put it into the end of the makefile:
clean: del $(ObjectFiles) $(ExeFile)If you are using gcc you might have to use rm instead of del, as it for some strange reason, seems to forget that it does know the del command.
Then you can run:
make -f makefile.mak cleanTo delete everything.
The process of creating and maintaining the list of dependencies is a bit boring, so why not use at tool to do it.
I have found a tool somewhere on the net, forgot where, I have modified it, download the modified version.
With this we can create a new target to our makefile:
MakeDep=makedep.exe makedepend: $(MakeDep) -n $(ObjectFiles)You can now create a list of dependencies by running:
make -f makefile.ext makedependThis will create a file called make.dep, so we have to include that into the makefile.
There is one catch here; we should not include make.dep if it does not exist, as we migth just be about to make it.
How to solve this, depends on the make program.
With VisualC++ and nmake it can be done by:
!if exist("make.dep") !include "make.dep" !else !MESSAGE "Dependency file does not exist" !endifThis will print "Dependency file does not exist" if make.dep does not exist, make.dep will be included otherwice.
GNU make:
ifneq ($(MAKECMDGOALS),makedepend) include make.dep endif$(MAKECMDGOALS) is a variable set by make containing the target you are building, if you are building dependencies it will not include the dependecy file, if you are not building dependencies and make.dep does not exist, make will show an error message and exit.
I have not found a proper way to do this with Borlands make, you could do:
!ifdef SKIP_MAKEDEP !message make.dep skipped !else !include make.dep !endifThis will requere you to add -DSKIP_MAKEDEP to the command line when running make, if you don't want to include make.dep.
For Digital Mars there does not seem to be any solution at all, you just include the file:
include make.depMake will stop if make.dep does not exist, so you have to create a empty make.dep first time.
Don't forget to tell makedep where it is to search for include files.
Now I will show how to add an icon to the program.
For this we need a .ico file and a reseource file.
If the name of the .ico file is calc.ico the reource file should contain this one line:
129 ICON DISCARDABLE "calc.ico"The number is more or less random selected, we don't need it elsewhere.
Now we need to "compile" this resource file and link it with the other .obj files.
For this we need a resource compiler, each compiler has its own, so the syntax for using it differs. For all, if the resource file is called calc.rc the output will be calc.res.
Now we can add two lines to the makefile, to compile the .rc file
First BorlandC:
RC=brc32 .res.rc: $(RC) -r -32 $*.rcThen Digital Mars:
RC=rcc .rc.res: $(RC) -D__NT__ $*.rcAnd GCC:
RC=windres %.res: %.rc $(RC) -i $< -I rc -o $@ -O coffAnd finally Visual C++:
RC=rc .rc.res: $(RC) $*.rcNow the calc.res file can be created, so we need to link to it.
First, Borland. Here we need to use ilink32.exe to link and not bcc32.exe, the command line will need to be changed:
Link=ilink32 ResourceFile=calc.res $(ExeFile): $(ObjectFiles) $(ResourceFile) $(Link) c0x32.obj $(ObjectFiles),$(ExeFile),,\ import32.lib cw32.lib,,$(ResourceFile)Note this is for a console application, if you are linkling a windows application you need to use c0w32.obj.
The line above starting with $(Link) ends with a \ this will get make to treat this line and the next line as one line. This feature can be used everywhere in the makefile to split long lines.
Next GCC:
ResourceFile=calc.res $(ExeFile): $(ObjectFiles) $(ResourceFile) $(Link) $(LinkFlags) $(ObjectFiles)\ $(ResourceFile) -o $(ExeFile)Then Digital Mars:
ResourceFile=calc.res $(ExeFile): $(ObjectFiles) $(ResourceFile) $(Link) $(LinkFlags) $(ObjectFiles) $(ResourceFile) $(ExeFile)And VisualC++:
ResourceFile=calc.res $(ExeFile): $(ObjectFiles) $(ResourceFile) $(Link) $(LinkFlags) $(ObjectFiles) \ $(ResourceFile) /Fe$(ExeFile)I have put everything into a .zip file.