Design of a small math utility library

We consider the design of a simple math utility library. Such libraries, with more efficient ones with much enhanced functionalities, already exist and is done here just for illustration purposes.

Features of the library

This will be a library of  3 dimensional vectors and matrices.


Directory structure for the code

As discussed in the class. We will organize the code for the library in the following fashion.

(Note: -  If your project produces some binary (ELF) executables then you should have a directory called bin to house that.)

A look at the header files

The code to go in a header usually includes some or all of the following:

Additionally, when using C++, templates and inline functions usually need to be in the header file.

Checkout this place for an implementaion of the math utils stuff. Note the directory structure. Go to the directory include to see the header file for the Vector stuff. Also not that the file include/MyVector.h
has the comments written in some specific style. This will be useful for generating documentation with Doxygen. More on this later but in the meantime you can have a look at the generated documentation.

A look at the source files

The code to go in a source file usually implements the functions defined in the corresponding header file.
Checkout this place and go to the directory src to see the implementation of the different functions of the Vector class as specified in its header.

    The tests directory contains a file testMyMath.cpp. Writing such testcode is important as they give you a confidence that a particular module is entomologically compliant

Howto get all the stuff made?

If you have been ploughing through the the MyMath directory, I assume that you have been doing so, you must have noticed some files called Makefile. They are absolutely vital for your survival as a programmer. Makefiles offer the following advantages for handling projects where the code is distributed over many files.
For detailed information on Make you should read the the manual

You can start by looking here for some makefiles with increasing level of complexity. The Makefile-1 is the simplest and does almost nothing apart from just to compile the programes. The other makefiles, Makefile-2,Makefile-3,.. etc are increasing in their comlexity and efficiency. For now I would just describe the different stuff going on in the Makefiles in MyMath
Consider the top-level Makefile in the MyMath directory.

It looks like this

TOPDIR          =$(PWD)
OBJDIR =$(TOPDIR)/objects
INCLUDES =-I$(TOPDIR)/include/


CPP =g++
CPPFLAGS =-Wall -Wno-unused -g $(INCLUDES)

OBJ_EXT = .o
OBJ_OPT = -c

all: subdirs

lib: $(LIBDIR)/libMyMath.so

subdirs: $(SUBDIRS)
$(MAKE) -C $@

.PHONY: subdirs $(SUBDIRS)

$(LIBDIR)/libMyMath.so: subdirs
$(CPP) -shared $(OBJDIR)/*.o $(LDFLAGS) -o $@

tests: subdirs testdir

testdir: $(TESTDIRS)
$(MAKE) -C $@
.PHONY: testdir $(TESTDIRS)

rm -f *~ core;rm -f lib/*
@for T in $(SUBDIRS); do make -C $$T $@; done
@for T in $(TESTDIRS); do make -C $$T $@; done
rm -f $(TOPDIR)/objects/*.o
rm -f $(TOPDIR)/include/*~
rm -f $(LIBDIR)/libMyMath.so

.PHONY: clean

TOPDIR          =$(PWD)
Here a variable $TOPDIR is defined. All the other directories are defined relative to this varibale. Generally such a variable would be defined in a top-level Makefile as is this one. It is not advisable to `hard-code' the base-directory as it hampers portability. If you hard-code, the next time you hand over your source code to some one else you will have to give him a detailed list of the changes to be made to the different Makefiles. With such a scheme the list is going to be smaller if not unnecessary.

SUBDIRS         =$(TOPDIR)/src/
Here a variable $SUBDIRS is defined. It is a variable referring to your source files. Note the reference to the source directory is in reference to the TOPDIR variable.

OBJDIR          =$(TOPDIR)/objects
Here a variable $OBJDIR is defined which is the location to house the object (.o) files produced by the compilation.

INCLUDES        =-I$(TOPDIR)/include/

Here a variable $INCLUDES is defined. It points to the location where the header files (.h) files are
 to be found. Note that we have prepended a `-I' to the location. Thus this variable itself can be
passed as an argument to the g++ command as g++ $(INCLUDES) when we want to compile. However another way to
do this (perhaps a proper way by some reason or logic) would be to define a separate variable called $INCLUDEDIR
and make it point to the include directory. Then one can define the $INCLUDES variable as -I$(INCLUDEDIR)


Here the variable $LDFLAGS is defined but its r.h.s. is empty in its definition. This is kept here more for completeness
and can be stuffed with options that would like to pass to the linker.


Here the variable $BINDIR is defined which is the directory to house the elf executables produced by the compilation.
In this example we donot produce such executables.


Here the variable $LIBDIR is defined which is the directory to house the library files produced by the compilation.
In this example we create the shared object libMyMath.so


Here the variable $TESTDIR is defined where we put all our test codes.

CPP =g++

Here the variable $CPP is defined. This is the name of the compiler to be used for the compilation.

CPPFLAGS =-Wall -Wno-unused -g $(INCLUDES)

Here the variable $CPPFLAGS is defined. This contains all the arguments given to the compiler. Note, try to use
the -Wall options (it dumps all warning) although the output will not be interesting if you code loosely. But anyway
they are helpfull for debugging purposes.

OBJ_EXT = .o

Here the variable $OBJ_EXT defines the extensions to be used for the object files. You will see later on how this is used.


Here the variable $EXE_EXT is defined which defines the extensions to be used for the executables. The r.h.s is kept blank.

OBJ_OPT = -c

Here the variable $OBJ_OPT defines the options to be passed to the compiler for creating objects (.o)


Simply by being mentioned as a target, this tells make to export all variables to child processes by default.
This is used for comminucating the varibles to the sub-make, in a recursive make. This can also be done by
explicitly setting the environment variables.

all: subdirs

This defines the first rule. The target all depends on the target subdirs.

lib: $(LIBDIR)/libMyMath.so

This defines a rule which says that the target lib depends on $(LIBDIR)/libMyMath.so i.e. ./lib/libMyMath.so
Thus saying make lib from the toplevel directory will create libMyMath.so.

subdirs: $(SUBDIRS)

The target subdirs depends on the target $(SUBDIRS).

$(MAKE) -C $@

The rule to 'make' target $(SUBDIRS). Here $@ is the makefile variable for target file name. We also use $(MAKE) variable
instead of explicitly saying make. This ensures several stuffs. It uses the same version of make in all the subdirectories, i.e the
version of make that was used to invoke the top-level Makefile. Also note that this is a multiple target rule as $(SUBDIRS) can contain many
stuffs. In our example it just holds the src directory, however we might have multiple source directories and they can all be put in $(SUBDIRS).

.PHONY: subdirs $(SUBDIRS)

This is a PHONY target. Read about PHONY targets in the Manual. It is not the name of any file that is being produced.
It is some command that we want to be executed when we go about doing explicit invocation. Lets stop and take a close
look at what is going on here. We concentrate on the above part of the Makefile.

subdirs: $(SUBDIRS)

$(MAKE) -C $@
.PHONY: subdirs $(SUBDIRS)


Let us consider a situation in which we have a huge collection of source files. Some of them are in
, some in src2, and the rest in src3. We want to have a recursive make from a directory above these
three directories. Let us look at the first way, the naive way

SUBDIRS = src1 src2 src3
for i in $(SUBDIRS); do \
$(MAKE) -C $$i;\

Now when we say make subdirs the recursive make happens (courtesy '-C' ) in all the subdirectories.
But wait.

	There might be errors in src1 and compilation fails there but Make continues happily with 
src2 and src3 unless the shell is explicitly told to bail out upon error. Remember Make keeps
on spawnining shells to do the stuff.
	Also you cannot do stuff like make -j2 and run the make parallely on two processors (if that 
is your system has one) as there is only one rule.

However in our makefile we have done things differently. In our case we have multiple rules. (Thus
parallelization is just typing that extra -j2 or -jN after make). Since separate shells are spawned to
handle the make on separate directories we have a handle on the error too.

$(LIBDIR)/libMyMath.so: subdirs
$(CPP) -shared $(OBJDIR)/*.o $(LDFLAGS) -o $@

Here we have said that the target $(LIBDIR)/libMyMath.so depends on subdirs. So it has to go and check
how to make subdirs. Having done that it proceeds to make the shared library. The second line in more readable form is

g++ -shared /home/subhajit/MyMath/objects/*.o -o /home/subhajit/MyMath/libs/libMyMath.so

The rest of the Makefile essentially explains how the tests target is to be built and on what does it depend. Also
it goes about defining the purging process, how to clean or remove all the junk.

Let us now go into the src directory to check the Makefile there which is recursively invoked by this
top-level Makefile. It looks like this. There's some sed stuff going on here and incase you are curious
about what or how sed check this classic sed tutorial



all: $(OBJS) $(OBJSC)

-include $(SRCS:.cpp=.d)

rm -f *~ core *.d

$(CPP) $(CPPFLAGS) $(INCLUDES) $(OBJ_OPT) $< -o $@

%.d: %.cpp
set -e; \
| sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
[ -s $@ ] || rm -f $@

Personal Information
TA Work
Hidden Line
LEGO ROBOT Assignment

Go to the Department of Computer Science and Engineering, IIT Delhi  Home Page.
This page was last updated on 05 August, 2005.

Valid HTML 4.0!