|
man
pages for the
compiler to learn about its possibilities. For instance: the
-frepo
option for g++ is default behavior of the SGI CC
compiler, and the g++ -Wall
option means almost the
same as the -fullwarn
option to the SGI compiler.
One of the advantages of the g++ compiler is that it exists on all the DAIMI Unix machines and behaves the same on all the platforms. Much documentation about g++ exists in the Emacs info system (choose "browse manuals" in the Emacs help menu).
Simple compilation of a C++ program
g++ -Wall -g program.cpp -o program
This compiles the C++ program in program.cpp
into
executable code in the file program
. The command line
options means the following:
On the other hand, -O can confuse gdb during debugging...
program
:
Remember
When you want to execute the above program from the command line, use
the command
./programand not just:
program
Many beginning Unix programmers have burnt their fingers by typing
test
and finding that this didn't work as they expected, as this command
usually executes the program /bin/test
and not the
program in the current directory.
Conditional compilation
When using conditional compilation and the C preprocessor it is useful
to know about the -D option of the C and C++ compiler. For example:
g++ ... -DFOO ... pgm.cppThe macro FOO is now defined during the compilation of
pgm.cpp
. It can be tested using #ifdef FOO
... #endif
. The use may also be a little more advanced:
g++ -DDEBUGLEVEL=7 ...used together with:
#if DEBUGLEVEL > 4 .... #endifMacro names are traditionally written in ALLCAPS.
When debugging it may be a good idea to have a run-time variable deciding how much debug info the program should print out. One example could be:
int func(double x) { #ifdef DEBUG if(debuglevel > 0) { printf("entered func\n"); } if(debuglevel > 3) { printf("x = %f\n", x); } #endif ... }Normally one would compile the program with -DDEBUG until completely sure that the program is bug-free.
Getopt
Most programs under Unix need to parse command line options of the
type -f
, -x argument
. There is no need to
re-invent the wheel here. The standard C library provides the function
getopt()
to parse this kind of command line options. See
man 3 getopt
. An example:
#include <getopt.h> int main(int argc, char *argv[]) { int ch; ... while ((ch = getopt(argc, argv, "x:f"))) { switch (ch) { case 'x': /* option -x was used */ printf("argument = %s\n", optarg); break; case 'f': /* option -f */ break; default: /* error */ break; } } }
M-x gdb
Emacs then asks for the name of the program that you want to debug. It should have been compiled with the -g option as described above. Emacs opens a buffer called *gud-...* (for Grand Unified Debugger). You may now set break-points in the program and run it under gdb. See how to use the gdb from within Emacs in the Help menu (Describe mode).
Here is a small sample of common gdb commands:
Gdb can do much more, and is described in the Emacs info system (C-h i) or choose "browse manuals" from the Help menu.
Assertions
Assertions can be a very useful thing when debugging your
software. They mostly help to check program invariants and find errors
earlier that would otherwise be possible.
In the beginning of your code you include assert.h:
#include <assert.h>For instance, to check that the function foo() is always called with a positive argument, you may write:
int foo(int n) { assert(n > 0); ... bla bla ... }
At run-time the assert() macro checks that n is positive, and if not the program aborts with an error message and a core dump that can be used to find the error:
Assertion failed: n > 0 at line 545 in "program.c"
It is good practice to have assertions checking the pre-conditions of all parameters to all functions in your program. Real consistency-check routines may also come in handy if you have complicated data structures with invariants that need to be maintained!
It is important not to change the state of you program within the assert macros. The assertions can be switched off at compile time (with -DNDEBUG) when you are sure that the program works, and the assertions will then not be included in the program. Statements like:
assert(n = 0);are therefore a big no-no!
Make
Make is a valuable tool when you develop programs consisting of
more than one or two files. Makefiles are used to collect the commands
used to compile and link a larger program complex, and to ensure that
only the files that have been changed are re-compiled. This is a huge
advantage when ones programs gets larger than maybe 1000 lines.
Makefiles specify dependencies and how to get from source files to the finished executable.
Here is a simple Makefile
. You may copy it from this template.
# Comments are nice # Here a macro definition specifying the source files SRC = modula.cpp modulb.cpp main.cpp # another macro, as SRC, but replace .cpp by .o OBJ = $(SRC:.cpp=.o) CC = g++ CFLAGS = -Wall -O -g # Here is the rule of how to get from a .cpp file to a .o file: # The $< macro expands into a source file name .cpp.o: $(CC) $(CFLAGS) -c $< # a dependency: to make the target "all", first do the target "pgm" all: pgm # another dependency: to make the target pgm, make the files that OBJ # expands to, here "modula.o modulb.o main.o", then link the object # files with the $(CC) command into the executable pgm pgm: $(OBJ) $(CC) $(OBJ) -o pgm # How does make find out how the $(OBJ) object files are made? It has # default rules for this, eg. the .cpp.o rule above.
Commands in Makefiles start with a TAB character, not spaces, but
ASCII 9. That is, the first character in the line $(CC) $(OBJ)
-o pgm
above is a TAB character. If you paste from an xterm
into emacs, then TABs will be converted into spaces and the make
commands will not work. You may recognize a TAB character in emacs
when the cursor jumps from the start of the line to the first $
character in one jump when you use the cursor keys.
You will often need more advanced dependencies among files. For example if modulea.h contains definitions that are used (#included) in both modulea.cpp and moduleb.cpp; when modulea.h changes then both modulea.cpp and moduleb.cpp need to be recompiled. You may use gcc to find these dependencies:
At the end of the above Makefile you append the following line:
include dependenciesNo # or quotes here...
You now execute the following command whenever you change includes in you programs:
gcc -MM *.cpp > dependenciesGcc then computes which .cpp files include which .h files, and writes the dependencies in to the dependencies file, which is included in the Makefile by make.
To re-compile your entire program complex you now just write:
makeand make knows which modules to re-compile.
M-x compile
man 3 <name of function>looks up in the third section of the on-line manual.
man -k <keyword>looks in the table of contents for <keyword>, for instance "
man -k memory
".
GCC, gdb, and many other tools are described in the Emacs info system. Choose "browse manuals" from the help menu or enter info-mode using "C-h i".
Include files
Standard include files (header files) referred to in C++ as for example
#include <stdio.h>are to be found under /usr/include/, /usr/include/CC/ and /usr/include/g++-include/. Reading these may be useful at times. Your own include files are used as:
#include "foobar.h"Note the use of quotes instead of angle brackets. The C++ compiler will look for these files in the current directory or in directories specified at the command line as
-I<directory>
.
A couple of things that the fresh C++ programmer should know: You do
NOT use include files to divide your code into smaller parts. You do
NOT include .cpp or .c files. You include .h files with common type
definitions, constants and prototypes in a number of .cpp files that
are then compiled separately and linked together at last. See below.
The compilation process
In general a C++ consists of a number of modules (.cpp files) that are
compiled separately. This is coordinated by a Makefile as described
above.
The figure below illustrates how a program consisting of two modules
p1.cpp
and p2.cpp
are compiled and linked
into a executable program a.out
.
p1.cpp ---> CPP ---> CC1plus ---> AS ---> p1.o \__ LD ---> a.out / p2.cpp ---> CPP ---> CC1plus ---> AS ---> p2.oEvery module is separately sent through CPP (the C preprocessor, taking care of all the #define and #include directives and macro expansion). The output of CPP is one large C++ file (you may see this expansion using "g++ -e file.cpp").
CC1plus is the real C++ compiler which translates from C++ to assembly. AS is the assembler compiling symbolic assembly into binary code in the .o object file.
The linker LD takes the object files which may refer to each other to various libraries and links them together into the final executable. Nowadays the libraries are seldom linked into the executable, only a stub is linked in and the real library is dynamically linked in when the program is started.
G++ (and CC) is a front end to all of this process. G++ knows, for example, that it need only run the linker on a set of .o files. The -c option to G++ tells it to stop before the link phase.
Standard Template Library
The Standard Template
Library (called STL) is a standardized C++ template library that
implements a lot of common data structures: dynamically sized strings,
singly- and doubly-linked lists, vectors, sets, hash tables, maps
etc. It is a good idea to learn to use the STL because it can save a
lot of programming time.
Here is a small example of using a STL map which is implemented by a red-black tree:
#include <string> #include <map> using namespace std; // the STL types are in the std namespace typedef map<string, int> StringMap; StringMap months; months[string("January")] = 31; months[string("February")] = 28; months[string("March")] = 31; months[string("April")] = 30; ...
Special C++ considerations
C++ contains a lot of powerful features: operator overloading, method
overloading, default parameter values, special memory allocator
operators, templates, exception handling, reference types and much more.
Here are some notes about my personal opinion about when to use them:
and not:int MyClass::f(const OtherClass& obj) {...}
int MyClass::f(OtherClass& obj) {...}
cvs update file.cwhich means that the most recent commited changes are merged into her local copy of file.c. Others in the group can edit their local copy of file.c simultaneously. When she is finished editing (and can compile the file) she says:
cvs commit file.cwhich will commit her changes into the respository. Commit starts an editor where A can write a message about what was changed. If others have commited changes to file.c in the meantime she will have to do a
cvs updateto merge their changes into her local copy of file.c first.
The only commands frequently used are "cvs update" and "cvs commit". "cvs diff" may also be useful to see the changes made between different versions.
CVS means Concurrent Version System, and builds on top of RCS (Revision Control System).
How to get started?
A group member is selected to keep the repository. He then makes a new
empty repository like this:
cvs -d /users/XXXX/project/repository initThis results in the creation of a /users/XXXX/project/repository/CVSROOT directory.
In their .login
file each group member should have a line like:
setenv CVSROOT /users/XXXX/project/repositotry ^^^ replace by suitable user nameTo put existing files from an examples/ directory into the CVS repository you must first remember to clear out all binaries and temporary files from the examples/ directory before importing the sources into CVS. Then do:
cd .../examples/ <--- for instance cvs import ex1 foo barEx1 is the name of the CVS module, that is, it refers to the bunch of files under examples. Foo and bar are vendor and branch tags. This will create a repository/ex1/ directory which will contain the revisions of the example program complex.
You may now check out the module from CVS by:
cd /somewhere/else cvs checkout ex1This will create a directory ex1/ where local copies of the source files will exist.
In order for this to work for all members of a group, the keeper of the repository should give permission to read and write the repository to the group members.
New files are added to the repository by
cvs add fil2.cSee also "man cvs" or read the info-pages in Emacs.
Peter Ørbæk, poe@daimi.au.dk, August 2001
Modified by Gerth Stølting Brodal, gerth@cs.au.dk, September 2004