How to use Libraries

For some reason I’m never able to use external libraries in any language. I’m looking for instructions/explanations of how to use external libraries, as well as how they work. When I search online, I get fragments that never seem to apply to whatever library I download and try and use. I work on both a mac and a pc, and C++ examples are fine. I use eclipse IDE with the C++ plug in. If there are instructions that apply to all libraries that would be great.

Answer

Say you have a class Unuseful defined as follows:

File Unuseful.h:

class Unuseful {
public:
    void printUnusefulStatement();
};

File Unuseful.cpp:

#include "unuseful.h"
#include <iostream>

void Unuseful::printUnusefulStatement()
{
    std::cout << "Hello world!" << std::endl;
}

Now, you have another class that needs printing unuseful statements:

Unuseful u;
u.printUnusefulStatement();

This means that you want to use an external library containing the specific implementation (printUnusefulStatement) that you want to include in your code.

You may use this library in two ways:

  1. By providing the source code to the compiler
  2. By providing a binary file (which had been previously compiled for your architecture), to the linker

Case 1: using a library at compile time

This is the simplest case. You have the source code of the library you have to use and you simply have to compile it together with your existing code (say main.cpp file). Typically you are the author and user of the library (a class that accomplishes a task you need).

Compiling with this command:

g++ main.cpp unuseful.cpp

allows you to use the implementation you need in your main.cpp file.

Case 2: linking a library

More often than Case 1, you don’t have the source code of the library you want to use. You only have the header file (Unuseful.h, to continue with the example) and a static or shared library (probably[*] libunuseful.a and libunuseful.so files, respectively).

The static library is an archive of object files (*.o) that are linked inside your final executables, the shared libraries instead are loaded dynamically – at run time (look at this page for a better understanding of the difference).

Static libraries are created by simply archiving the *.o files with the ar program:

# Create the object files (only one here)
g++ -c unuseful.cpp
# Create the archive (insert the lib prefix)
ar rcs libunuseful.a unuseful.o

Shared libraries are created with the g++ -shared option:

# Create the object file with Position Independent Code[**]
g++ -fPIC -c unuseful.cpp
# Crate the shared library (insert the lib prefix)
g++ -shared -o libunuseful.so unuseful.o

Let’s suppose now you have the Unuseful.h file and the shared library (libunuseful.so file) and you have a main.cpp file that instantiates a Unuseful object and calls the printUnusefulStatement method.

If you try to compile this file (g++ main.cpp) the linker will complain because it cannot find the printUnusefulStatement symbol.

It’s time to use the library:

g++ main.cpp -L. -lunuseful

The -L option tells the linker where to search for library files and the -l flag tells the linker the name of the libraries to be used (without the lib prefix).

Now the executable (a.out, because I didn’t specify a different name) is created, and you have used a library to implement a functionality you needed (printUnusefulStatement).

Since the shared library is loaded at run-time, the execution of the a.out executable may fail because the system is not able to find the library. Typically this can be solved by appropriately setting an environment variable indicating which paths to use to search for dynamic libraries:

# Set the LD_LIBRARY_PATH [*]
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

Done, now your executable has been compiled and it will be able to run and load the library it needs.

Conclusion

This is a rapid overview on libraries which I hope can help you understand how they are used and provided to others.

There are many many aspects that should be investigated in more detail, if you are interested: g++ options when creating shared libraries, ar options, environment variables, the shared libraries format and so on.

[*]: In a Unix environment

[**]: If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on the m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines. [From the g++ man page]