Start-up notes on Programming in C++

By Simon Connell

1.  Windows or Linux ?

  • Windows
    • You can code in windows with MS VC++ (free version). This is one option which is compatible with ROOT (excellent framework for scientific computing). However, unless you are a masochist, eventually you will wish you were rather using Scientific Linux. Also, when teaching, I always found those who were programming with g++ (in Linux / Unix) had less hassels (easier time debgging, more scalable computing).
    • Since Windows 10, there is WSL or Windows Subsystem for Linux. Windows 11 has WSLg, even nicer. You can graduate more easily eventually to a Linux envirnoment. I recommend this for windows rooted newbies .... Google is your friend. You will also be using the g++ compiler. I prefer this for students starting off ....
  • Linux / Unix
    Here you will use the g++ compiler
    • You can take your windows laptop and do
      (Neither of these are for the faint-hearted .. Google around and read a bit before you implement this.)
      • Dual Boot (Linux and Windows on one laptop).
      • Use Windows Subsystem for Linux (WSL) available for W10 and W11. Works best in W11. Now a reasonable option in Windows
      • Use a virtual environment like VMware (where you put Linux in a vitual machine inside a Windows system)
    • Have access to a Linux server, and run remotely on that from any platform.
      • Use a Secure Shell client (like ssh from Linux, or Putty for windows) to have a remote shell
      • Use a secure copy client (like scp from Linux, or WinSCP for windows) to transfer files
      • Or even use jEdit from windows ... then you can run a nice edit session on a remote server. You must download some extra capacity via plugins to get the remote edit capability.
    • Use a Macbook and then you have a well supported unix platform - Mac OS X
    • Use any version of Linux, like Ubuntu, Susy, Debian ...
    • Consult Linux@CERN ... stay with CERN tested versions, particularly for ROOT and Geant4 and server level computing (simulation, large code systems)
      • Here the issue is what can run GPU code, ROOT and Geant4 in demanding applications, what is compatible with Clusters and CVMFS.
        • Amazing, as at the end of 2022, we can do this with Ubuntu 24, with special adaption dynamically of the gcc compiler

2.  Editor

  • Windows
    • Use notepad++ or something like that to edit under Windows.
    • The MS VC++ option of course has its own editor.
  • Linux
    • Lots of choice
      • vi - can be used esily from a text screen ... teach yourself from any site you can google for vi beginners. This is a serious editor. Even if you dont become an expert in it, its a good one to also have a basic knowledge of, as you can generally still use it minimalist or in emergency situations
      • emacs or xemacs ... teach yourself from any site you can google for vi beginners. This is a serious editor.
      • kate and gedit are nice lightweight starter-pack type editors.
The editors are like primitive word-processors to make documents as text files, but they have lots of features that are programming language aware. For exampe, sophisticated macro capabilities, C++ aware syntax highlighting and auto-indenting etc.
Usage
      <editor of choice> filename
Simply play around in the editor to learn its capabilities.
You may have to go to the menu to switch on the syntax highlighting

Go back to the top of the page

3.  Compiler

We are using the GCC compiler suite.
A typical compile only command is

  g++ -c <filename>.cpp

where '-c' specifies compile only. The machine code translation of the source code, which is non-executable and called object code, is output as <filename>.o. It is non-executable as it does not have the code inserted for complex system functions. This is done at the link stage, you access the standard libraries containing code for ordinary system services by deault.

A typical link command would then be

 g++ <filename>.o -o <filename>

Here the '-o' option specifies a non-default output filename; without it the compiler will produce an executable called a.out.

You may have to set the executable file permissions to executable yourself:

 chmod ugo+x <filename>

You can issue

 man chmod

to see the man-page of the chmod command. Producing the executable from the source in one compile and link step would be

 g++ <filename>.cc -o <filename>

To run your program, you should be able to simply type

 ./<filename>

assuming this is your executable from the previous step.

The compile command can get more sophisticated... For example

 g++ -g -O3 -mcpu=i686 -funroll-loops -Wall -fno-implicit-templates    //
       -Weffc++ -Wold-style-cas   <filename-1>.c  .... <filename-n>.c   //
       <filename-1>.o .....  <filename-n>.o    -I ../linux-gpib/include          //
       -L../linux-gpib/  /lib -lgpib -lfl  -L$(CERNLIB)  -lpacklib -l$(LIBF77)        //
       -lnsl  -L $(STDLIBS) -lm

Here, we specifiy paths to non-standard include files with '-I' and we specifiy paths to non-standard library files with '-L'.
We specify libraries with '-l', where the 'l' replaces the 'lib' prefix on the library, and the extension is omitted.

We also use system variables, where the compilation will only work if 'CERNLIB' and 'LIBF77" are defined to point to paths where the appropriate files may be found. Also, various compiler and linker switch options are set.

The '-g' caused symbolic information at the link stage to be used, so that you can run the code through the xgdb debugger.

The '-O3' is an instruction customising the code optimisation. The '-Wall' and '-Weffc++' customise the warning messages.

Go back to the top of the page

4.  Make utility

  1. The `make' utility automatically determines which pieces of a large program based on (multiple object files and library files) need to be recompiled, and issues commands to recompile them. You need a file called a "Makefile" to tell `make' what to do.
  2. Most often, the makefile tells `make' how to compile and link a program.
    1. Suppose you are busy with the development of a complicated application, there may be many source files (*.c, *.cc, *.c++,*.f etc) and header files (*.h) that need to be recompiled to produce object files (machine code, *.o).
    2. These object files must then be linked together with the default libraries, as well as other libraries, like in the example above.
    3. The compile and link comands will become very cumbersome. You may get the program working with one version of the compile - link instruction, but not others. In some weeks time, coming back to your development after a period of absence, you may find you can no longer get your code working, as you cannot remember exactly the compile - link details which were succesful.
    4. Also, if there is only one compile - link instruction, it will reprocess everything in the instruction, even if you only made one small change to just one of the source files. This can be time consuming for large development applications.
    5. The make utility will manage all this complexity. In general, if a C source file has changed, each changed C source file must be recompiled. If a header file has changed, each C source file that includes the header file must be recompiled to be safe. Each compilation produces an object file corresponding to the source file.
    6. Finally, if any source file has been recompiled, all the object files, whether newly made or saved from previous compilations, must be linked together to produce the new executable editor.

4.1  What a Rule Looks Like

A simple makefile consists of "rules" with the following shape:

TARGET ... : PREREQUISITES ...
        COMMAND
        ...
        ...
  • A target is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out, such as `clean' (*note Phony Targets::).
  • A prerequisite is a file that is used as input to create the target. A target often depends on several files.
  • A command is an action that `make' carries out. A rule may have more than one command, each on its own line.
  • Please note: you need to put a tab character at the beginning of every command line! This is an obscurity that catches the unwary.

Usually a command is in a rule with prerequisites and serves to create a target file if any of the prerequisites change. However, the rule that specifies commands for the target need not have prerequisites. For example, the rule containing the delete command associated with the target `clean' does not have prerequisites. A "rule", then, explains how and when to remake certain files which are the targets of the particular rule. `make' carries out the commands on the prerequisites to create or update the target. A rule can also explain how and when to carry out an action.

Note Writing Rules: Rules.

A makefile may contain other text besides rules, but a simple makefile need only contain rules. Rules may look somewhat more complicated than shown in this template, but all fit the pattern more or less.

4.2  A Simple Makefile

Here is a straightforward makefile that describes the way an executable file called `edit' depends on eight object files which, in turn, depend on eight C source and three header files.

In this example, all the C files include `defs.h', but only those defining editing commands include `command.h', and only low level files that change the editor buffer include `buffer.h'.

edit : main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
             insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
                insert.o search.o files.o utils.o

We split each long line into two lines using backslash-newline; this is like using one long line, but is easier to read.

To use this makefile to create the executable file called `edit', type:

 make

To use this makefile to delete the executable file and all the object files from the directory, type:

 make clean

In the example makefile, the targets include the executable file `edit', and the object files `main.o' and `kbd.o'. The prerequisites are files such as `main.c' and `defs.h'. In fact, each `.o' file is both a target and a prerequisite. Commands include `cc -c main.c' and `cc -c kbd.c'.

When a target is a file, it needs to be recompiled or relinked if any of its prerequisites change. In addition, any prerequisites that are themselves automatically generated should be updated first. In this example, `edit' depends on each of the eight object files; the object file `main.o' depends on the source file `main.c' and on the header file `defs.h'.

A shell command follows each line that contains a target and prerequisites. These shell commands say how to update the target file. A tab character must come at the beginning of every command line to distinguish commands lines from other lines in the makefile. (Bear in mind that `make' does not know anything about how the commands work. It is up to you to supply commands that will update the target file properly. All `make' does is execute the commands in the rule you have specified when the target file needs to be updated.)

The target `clean' is not a file, but merely the name of an action.

Since you normally do not want to carry out the actions in this rule, `clean' is not a prerequisite of any other rule. Consequently, `make' never does anything with it unless you tell it specifically. Note that this rule not only is not a prerequisite, it also does not have any prerequisites, so the only purpose of the rule is to run the specified commands. Targets that do not refer to files but are just actions are called "phony targets".

4.3  How `make' Processes a Makefile

By default, `make' starts with the first target (not targets whose names start with `.'). This is called the "default goal". ("Goals" are the targets that `make' strives ultimately to update. *Note Arguments to Specify the Goals: Goals.)

In the simple example of the previous section, the default goal is to update the executable program `edit'; therefore, we put that rule first. Thus, when you give the command:

 make

`make' reads the makefile in the current directory and begins by processing the first rule. In the example, this rule is for relinking `edit'; but before `make' can fully process this rule, it must process the rules for the files that `edit' depends on, which in this case are the object files. Each of these files is processed according to its own rule. These rules say to update each `.o' file by compiling its source file. The recompilation must be done if the source file, or any of the header files named as prerequisites, is more recent than the object file, or if the object file does not exist. The other rules are processed because their targets appear as prerequisites of the goal. If some other rule is not depended on by the goal (or anything it depends on, etc.), that rule is not processed, unless you tell `make' to do so (with a command such as `make clean').

Before recompiling an object file, `make' considers updating its prerequisites, the source file and header files. This makefile does not specify anything to be done for them - the `.c' and `.h' files are not the targets of any rules - so `make' does nothing for these files. But `make' would update automatically generated C programs, such as those made by Bison or Yacc, by their own rules at this time. After recompiling whichever object files need it, `make' decides whether to relink `edit'. This must be done if the file `edit' does not exist, or if any of the object files are newer than it. If an object file was just recompiled, it is now newer than `edit', so `edit' is relinked.

Thus, if we change the file `insert.c' and run `make', `make' will compile that file to update `insert.o', and then link `edit'. If we change the file `command.h' and run `make', `make' will recompile the object files `kbd.o', `command.o' and `files.o' and then link the file `edit'.

4.4  Variables Make Makefiles Simpler

In our example, we had to list all the object files twice in the rule for `edit' (repeated here):

edit : main.o kbd.o command.o display.o \
        insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o

Such duplication is error-prone; if a new object file is added to the system, we might add it to one list and forget the other. We can eliminate the risk and simplify the makefile by using a variable. "Variables" allow a text string to be defined once and substituted in multiple places later (*note How to Use Variables: Using Variables.).

It is standard practice for every makefile to have a variable named `objects', `OBJECTS', `objs', `OBJS', `obj', or `OBJ' which is a list of all object file names. We would define such a variable `objects' with a line like this in the makefile:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

Then, each place we want to put a list of the object file names, we can substitute the variable's value by writing `$(objects)' (*note How to Use Variables: Using Variables.).

Here is how the complete simple makefile looks when you use a variable for the object files:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
             cc -o edit $(objects)
main.o : main.c defs.h
             cc -c main.c
kbd.o : kbd.c defs.h command.h
             cc -c kbd.c
command.o : command.c defs.h command.h
             cc -c command.c
display.o : display.c defs.h buffer.h
             cc -c display.c
insert.o : insert.c defs.h buffer.h
             cc -c insert.c
search.o : search.c defs.h buffer.h
             cc -c search.c
files.o : files.c defs.h buffer.h command.h
             cc -c files.c
utils.o : utils.c defs.h
             cc -c utils.c
clean :
             rm edit $(objects)

4.5  Using implicit rules and variables to further simplify the Makefile

Any modern version of make, and in particular the GNU version on Linux, does actually know something about most common operations done to create a program; so, if you just specify a dependence like

 file.o:file.c file.h anotherfile.h 

it will automatically use a command

 $(CC) $(CFLAGS) -c file.c

GNU make has even more aces in its sleeves, amongst which the automatic variables like $^, $@ etc are most useful, as they allow for very "clean" makefiles:

target = edit
objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

$(target) : $(objects) someotherfile.o
             $(CC) $^ -o $@
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

clean :
             rm $(target) $(objects)

You can read all about this and more on make by typing on the command line

  info make

4.6  General Purpose Make File

  • I usually use this Makefile as a general purpose one .... it has the capacity to compile against the ROOT libraries. If you will use

the ROOT capacity, then uncomment lines about ROOT (read in the file).

Go back to the top of the page

5.  Hello World Program

helloworld.cc

/*************************************************************************
 * Program Name : Helloworld            Written by : S.H.Connell         *
 *************************************************************************/

/****************************
 *     Include files        *
 ****************************/

#include <iostream>
using namespace std;

/*************************************************
 * Function : main                               *
 *************************************************/
int main(void)
{
    std::cout << "\n\n Hello World   \n\n " ;
    return 0;
}

Makefile (You will have to put in the tabs yourself if you cut and paste this code segment)

# Lines starting with the pound sign are comments.
#
# These are the two options that may need tweaking

EXECUTABLE = HelloWorld
LINKCC = $(CXX)

#Uncomment this next line if you want to link to the ROOT FrameWork
#INCLUDES = -I$(ROOTSYS)/include

# You can modify the below as well, but probably
# won't need to
#

# CC is for the name of the C compiler. CPPFLAGS denotes pre-processor
# flags, such as -I options. CFLAGS denotes flags for the C compiler.
# CXXFLAGS denotes flags for the C++ compiler. You may add additional
# settings here, such as PFLAGS, if you are using other languages such
# as Pascal.

CPPFLAGS =

#Uncomment this next line if you want to link to the ROOT FrameWork
#LDFLAGS = -L$(ROOTSYS)/lib `root-config --glibs`

CC = gcc -g
#CFLAGS = -Wall -O2
CFLAGS = -Wall

CXX = g++ -g
CXXFLAGS = $(CFLAGS) $(INCLUDES)

SRCS := $(wildcard *.c) $(wildcard *.cpp) $(wildcard *.C)
OBJS := $(patsubst %.c,%.o,$(wildcard *.c)) \
	$(patsubst %.cpp,%.o,$(wildcard *.cpp)) \
	$(patsubst %.C,%.o,$(wildcard *.C))
DEPS := $(patsubst %.o,%.d,$(OBJS))

# "all" is the default target. Simply make it point to myprogram.

all: $(EXECUTABLE)

# Define the components of the program, and how to link them together.
# These components are defined as dependencies; that is, they must be
# made up-to-date before the code is linked.

$(EXECUTABLE): $(DEPS) $(OBJS)
	$(LINKCC) $(LDFLAGS) -o $(EXECUTABLE) $(OBJS)

# Specify that the dependency files depend on the C source files.

%.d: %.c
	$(CC) -MM $(CPPFLAGS) $< > $@
	$(CC) -MM $(CPPFLAGS) $< | sed s/\\.o/.d/ >> $@

%.d: %.cpp
	$(CXX) -MM $(CXXFLAGS) $(CPPFLAGS) $< > $@
	$(CXX) -MM $(CXXFLAGS) $(CPPFLAGS) $< | sed s/\\.o/.d/ >> $@

%.d: %.C
	$(CXX) -MM $(CXXFLAGS) $(CPPFLAGS) $< > $@
	$(CXX) -MM $(CXXFLAGS) $(CPPFLAGS) $< | sed s/\\.o/.d/ >> $@

# Specify that all .o files depend on .c files, and indicate how
# the .c files are converted (compiled) to the .o files.

clean:
	-rm $(OBJS) $(EXECUTABLE) $(DEPS) *~

explain:
	@echo "The following information represents your program:"
	@echo "Final executable name: $(EXECUTABLE)"
	@echo "Source files:     $(SRCS)"
	@echo "Object files:     $(OBJS)"
	@echo "Dependency files:   $(DEPS)"

depend: $(DEPS)
	@echo "Dependencies are now up-to-date."

-include $(DEPS)

Go back to the top of the page

Once you have the files, you can run

 make

and

 ./helloworld

6.  cmake

  • Larger or smaller projects can now be built with cmake
    • especially fantastic for very large projects
    • for example, installing root from sources ..... this is the preferred method.

7.  Some Hints

When using mixed language programming, like including C code in a C++ environment, you need to insert some additional code in the C-style header files.

  • For example, for C-style Numerical recipes, include files adjusted for mixed language programming in a C++ environment ...

Go back to the top of the page