Intro to using GNU/Linux for C++ Programming - Part 2: GCC & Python

The GNU Compiler Collection

GCC online documentation

The GNU Compiler Collection utility 'gcc' was originally a C compiler, but is now a collection of compilers and tools for compiling many languages, including C++. The GNU C++ compiler g++ provides a front-end to gcc that compiles both C and C++ files as C++ and links to the std C++ libraries. Other tools in the GCC are the GNU C (and C++) Pre-Processor (cpp), and the GNU Linker (ld).

You can use the graphical text editor gedit to open multiple files for editing:
$ cd ~/Documents
$ mkdir myproject; cd myproject
$ touch README mymain.cc
$ gedit README mymain.cc
Add what you want to the README and enter your preferred 'hello world' main function to the source file. This is all you need to compile and run:
$ g++ mymain.cc
$ ls -l
total 20
-rwxrwxr-x 1 yourname yourname 9096 2011-11-21 01:06 a.out
-rw-rw-r-- 1 yourname yourname 194 2011-11-21 01:06 mymain.cc
-rw-rw-r-- 1 yourname yourname 135 2011-11-21 01:06 README
$ ./a.out
Hello World
The command 'g++ mymain.cc' created an executable file 'a.out'. You can specify the name of the output file by adding '-o filename' to the end.
$ rm a.out
$ g++ mymain.cc -o runme
$ ./runme
...
Another way of writing the command is 'g++ -o runme mymain.cc'. This is more along the lines of the 'target: dependencies' format we will see when we look at makefiles. Two compilation options you should be aware of: -Wall 'warn all' will show you all the warnings generated during compilation. -g will output a debug version of your executable:
$ rm runme
$ g++ -Wall -g mymain.cc -o runme
An outline of the steps taken by gcc is as follows: Preprocessing>Compilation>Assembly>Linkage.

1. Taking your source code as input, the Preprocessor (cpp) gets the code ready for the Compiler: stripping out comments and taking care of Preprocessor Directives (#include, #define, etc.) and sends it to standard output (the resulting code is referred to as 'cpp-output').

2. The Compiler can then translate the cpp-output into human-readable assembly (.s file).

3. From this the Assembler can create machine code binaries AKA object files (.o file).

4. The final executable is generated by the Linker (ld), linking together all the object files and dynamic libraries, outputs an executable file.

For more in-depth explanations see links.

In the example above we had only one source file and one executable; there was no intermediate object file created so by default g++ went through all steps and output an executable file. However if g++ can't find main() in your source file, the linker will be unable to create an executable output.

With gcc/g++ we can use command line options to stop at any point in the process.
  • -c stop short of linking executable, output will be machine-code binary object file (.o)
  • -S stop short of creating binary object file, result will be human-readable assembly file (.s)
  • -E stop short of creating assembly, result will be output of Preprocessor (including a large amount of code pulled in from any included headers). This will be sent to the standard output unless an output filename is provided to g++ with -o

You don't have to try the following if you have included the standard header <iostream> because it will flood your console:
$ g++ -Wall -E mymain.cc
...
-or-
$ cpp -Wall mymain.cc
...
In the first case we use the option '-E' to tell gcc to stop after Pre-Processing, and in the second example we use the 'cpp' utility directly. Instead of printing the cpp-output, you can specify an output file and take a look at it that way (or use the '>' symbol to redirect to a file):
$ cpp -Wall mymain.cc -o mymain
-or-
$ cpp -Wall mymain.cc > mymain
An example first ten lines of mymain:
$ head mymain
# 1 "mymain.cc"
# 1 ""
# 1 ""
# 1 "mymain.cc"
# 1 "/usr/include/c++/4.6/iostream" 1 3
# 37 "/usr/include/c++/4.6/iostream" 3
       
# 38 "/usr/include/c++/4.6/iostream" 3

# 1 "/usr/include/c++/4.6/x86_64-linux-gnu/./bits/c++config.h" 1 3
If we do not including any system headers we can take a look at a smaller Pre-Processor output. For demonstration purposes I'm going to create a small header with a const variable defined and include that in mymain.cc:
$ rm mymain
$ vi mycode.hh
$ cat mycode.hh
#ifndef MYCODE_H
#define MYCODE_H

const int a = 99;

#endif
$ vi mymain.cc
$ cpp -Wall mymain.cc -o mymain
$ cat mymain
# 1 "mymain.cc"
# 1 ""
# 1 ""
# 1 "mymain.cc"
# 1 "mycode.hh" 1



const int a = 99;
# 2 "mymain.cc" 2

int main(){
 return 0;
}
If your file contains comments they will be replaced with blank lines, so your output may be formatted differently but still have the same overall outline.

Briefly, the first number after a '#' sign is a line number, followed by the file and an optional code. The optional code '1' means the preprocessor is descending into an included file (eg "mycode.hh" 1), and '2' that it is returning ("mymain.cc" 2). For more details see Section 9 of the CPP manual. If you have any '#define's in your files they will not be shown here. This is what the "<built-in>" and "<command-line>" are labelling: there are some built-in and (possibly) command-line #define s which we can see by using the -dD option. I won't reproduce the output here, but you could use the command line option '-dD' to see the #defines:

$ rm mymain
$ cpp -Wall -D PROG=1 -dD mymain.cc -o mymain
I added another option: '-D PROG=1' which you will see under the '#1 "<command-line>"' entry. That's all it's doing: defining PROG from the command line. '-dD' is changing the output so we are shown all the defines, the majority of which are under '#1 "<built-in>"'. Where did the #defines go if this is the default output headed to the Compiler? Spoiler: This is the output of the Preprocessor, so all the defines have been substituted in the code by their definitions.

This should give you some idea of the structure of a 'compilation unit' in C++. For each source file, a compilation unit includes the code in the source with all the headers tacked on to the front of it. A compilation unit doesn't know about the implementation of other compilation units beyond what is in a header, which is why things like function templates need to be defined in headers if any other compilation units are to make use of them. It isn't until Linking that calls to declared functions defined in other compilation units are resolved.

You can go ahead and edit the main function any way you like now that we are done looking at the Preprocessor, but make a new cpp-output file (without -dD) so it will include any changes you've made.

The next stage, 'Compilation' proper, takes as input the cpp-output and outputs a '.s' assembly file. A new command line option we need to use is '-x language' to specify the input type, otherwise you will get an error. In the previous step, we could have been explicit with our source file type 'cpp -Wall -x c++ mymain.cc -o mymain' but it is unnecessary since gcc uses the file extension '.cc' to determine that the input was a C++ source file. To be consistent, C++ cpp-output has its own name in gcc: 'c++-cpp-output'. To compile the cpp-output file 'mymain', the command would be:
$ g++ -Wall -S -x c++-cpp-output mymain
No '-o filename' needed (unless you want some other filename), the output of compiling with the '-S' option is filein.s: 'mymain.s'. This file is in an assembly language dependent on your CPU specifications, the contents of which is beyond the scope of this guide and is an intermediate file that is usually not written during compilation.

The next step, however, the Assembler produces an object file '.o' (machine-code binary) that is usually saved and can be used to check if changes to the source have been made for this compilation unit (in order to avoid re-compiling the entire program; if changes have only occurred in just a small number of units we can simply re-compile the ones that have changes).
$ g++ -Wall -c mymain.s
The option '-c' stops before linking, and the type of input is inferred from the extension '.s'. The output is an object file 'mymain.o'. If there are no other compilation units this is all we need to link and get our executable.
$ g++ -Wall mymain.o -o runme
$ ./runme
What if you have more than one compilation unit? Go ahead and define a function in a source file such as 'mycode.cc' and declare in 'mycode.hh' and include and call it from mymain.cc:
$ cat mycode.hh
#ifndef MYCODE_H
#define MYCODE_H

int foo();

#endif
$ cat mycode.cc
#include "mycode.hh"

int foo()
{
    static int mystatic = -12;
    return ++mystatic;
}
$ cat mymain.cc
#include 
#include "mycode.hh"

int main(){
    using namespace std;
    cout << "\nHello World\n";
    cout << "mycode foo(): " << foo() << endl << endl;
    return 0;
}
Cleanup the directory, create your object files, and link:
$ rm mymain mymain.s mymain.o
$ g++ -Wall -c mymain.cc mycode.cc
$ g++ -Wall mymain.o mycode.o -o runme
$ ./runme

Hello world!
mycode foo(): -11

$
We could have skipped creating the object files but we want to get used to them as you will see in Part 3.

Python

Python Documentation Index

Python 2.7.2 is included in the CD installation of Linux Mint 12. (For an introduction see The Python Tutorial). In Part 3 it will be used by the utility SCons to manage projects, but for now at least be aware that Python works as a powerful desk-calculator from the command line:
$ python
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 3+14
17
>>> 12*60
720
>>> pow(2,8)
256
>>> 0xff
255
>>> hex(0xff & 0xa3)
'0xa3'
>>> hex(0xff | 0xa3)
'0xff'
>>> hex(0xff ^ 0xa3)
'0x5c'
>>> 
$ 
The command to exit Python and return to a bash prompt is CTRL-D.

Part 3: GNU Make

No comments:

Post a Comment