What is the -I, the -L, and the –(little L)? Why am I getting so many errors? What’s going on underneath?

C and C++ libraries can be thought of as a conglomeration of functions and new keywords that you are able to use in your code.

To truly understand what libraries are made of, we should first refresh on header and source files.

 

Header and Source File Example

Header files contain declarations of functions and variables and often have the file extension .h.

Source files contain the definitions of these functions and often have the file extension .cpp or .cc.

As a brief refresher the example below contains a header, source file, and main file for displaying a Birthday.

SP Libraries 1

 

Birthday.h

Declares a class called birthday.

 

 

 

 

SP Libraries 2

 

Birthday.cpp

Defines the two Birthday functions specified above.

 

 

 

SP Libraries 3

 

main.cpp

Creates an instance of the birthday class, and calls the function printDate.

 

 

 

SP Libraries 4

 

The file structure consists of a main directory containing the .cpp files and the Makefile, and then a separate subdirectory containing the header file as shown:

 

 

Here is the Makefile to compile and run the code:

SP Libraries 5

make
./executable

Output:

SP Libraries 6

The Makefile is splitting up the compilation process into two stages.

First the .cpp files are being turned into object files with a .o extension, then there is a linking stage where the main.o and the Birthday.o files are linked together to create an executable.

But why was the $(INC) there? When is the $(INC) necessary?

In C++, the #include macro at the beginning of programs tells the preprocessor to replace the instance of #include with the contents of the file.

For example, the main.cpp file includes the Birthday.h header file so that it can reference the declarations of the functions Birthday and printDate.

These functions are not fully defined by the header file, but they are prototyped and able to be referenced without throwing syntax errors.

The new file main.cpp file after being preprocessed is pictured below:

SP Libraries 7

Understanding this, we realize that the preprocessor must be able to access the included file in the compilation.

The file is referenced as Birthday.h, and if the pre-processor were to search the current directory for the file, it would be unable to find it because relative to the main directory the Birthday.h file has the path Headers/Birthday.h.

Therefore, the -I flag tells the preprocessor where it should search for files when compiling.

In this case the flag is including the Headers folder, resulting in the discovery of the file and the replacement of the #include.

The Birthday.o file is created the same way with the same -I, as it must also reference the Birthday.h file for the function declarations it is defining.

Next, linking occurs. After the two object files are created, the Birthday.o object contains functions that the main.o file calls.

The Birthday.o object file now contains both the declaration and the definitions after the preprocessor inserted the contents of the .h file.

Now, we must “link” the two object files together so that the functions called by main.o can be executed from the definitions in Birthday.o.

There is no flag for this linking. They are simply linked together by listing the name of the files.

g++ main.o Birthday.o -o executable

 

Back to Libraries

How does this relate to libraries? Libraries are just a grouped together set of .o files.

This means that when compiling they require inclusion of library header files when you create a .o file, as well as a linking stage when creating an executable.

Let’s take a look at how we can link to a library called Google test, which performs modular code testing for files. For more information on Google testing visit our post here.

SP Libraries 8

Example file using #include

This file will simply run the Test to check to see if two plus two equals 4. This can be compiled by:

g++ -I(path to googletest)/include/ -c Test.cc

(Creates object file. The full path to the gtest.h header file is (path to googletest)/include/gtest/gtest.h)

g++ -pthread Test.o (path to libgtest.a)/libgtest.a

(Links to the library the same way as before, it is just a precompiled set of object files)

*Google testing uses pthreading underneath and the -pthread flag is unrelated to this tutorial

./a.out

An alternate way to link to the library is with the -L and -l commands.

g++ -I(path to googletest)/include/ -c Test.c

(Same as before.)

g++ -pthread Test.o -L(path to libgtest.a) -lgtest

(-L path to library *not including the library name* -l library name *excluding the prefix, lib, and suffix, .a*)

SP Libraries 9

WARNING:
The creation of an object file and linking must occur in two different steps.

The first step includes the preprocessor replacing all header files and checking for syntax/function references (the -I flag).

The next step is the actual linking to the definitions of the function references(the -L and -l flag).

There are ways that you can try to do these steps together, and sometimes it will work with flags like -WL, and -Wl, but this practice is considered unscalable, unprofessional, and tenuous, potentially outputting many cryptic errors.

 

Some libraries do not require explicit inclusion or linking becuase the compiler g++ already knows where to search.

For example, iostream is actually a header file which operates the same way as any other library.

It’s placement is computer specific, but mine is stored in /usr/include/c++/4.8/iostream along with other standard c++ header files.

The library itself is stored elsewhere, for me it is in usr/lib/gcc/x86_64-linux-gnu/4.8/libstdc++.a or libstdc++.so.

There are system variables in place which predefine search paths and linking paths for the compiler to follow by default.

In the case of iostream and the c++ library, the compiler already knows to include the header files and link automatically so that you don’t have to explicitly link for each program.

These default paths can be output to the console and the default paths can be adjusted for making it easier to link to a library for a project.

cpp -v

SP Libraries 11

(The default include path for your system)

ld –verbose | grep SEARCH

SP Libraries 10

(The default library search path *Takes the place of -L*)

One final important concept in this tutorial is that of static and dynamic libraries. As shown, some libraries can be linked to without using the -L and -l flags, simply by specifying the path to the library.

A static library can be compiled either way specified above, but a shared library cannot.

The major difference is that shared object libraries are loaded into a program at run time whereas static libraries are preloaded into the executable itself.

This make an executable compiled with a .a library self-contained whereas a program compiled with a .so library must be able to access the .so at runtime.

A .so library ALWAYS requires the -L and -l syntax because of the compilers need to know where the library is located and to link to it.

Because .a libraries can also function using the -L and -l flags, it is recommended that you stick with them for every link to a library.

Learning how to deal with libraries is crucial to figuring out how IDE’s are working underneath and can be an invaluable skill for any Linux based software development job.

I hope you enjoyed the tutorial and if you have further questions please post them below!