One thing that came to my mind frequently, while I was working with C++, was that how to link external libraries to my projects. A lot of the time, when I see a project on Github, I see the source code and a makefile, but I don’t know what I suppose to do with them. Do I need to compile them, to a special format? How do I compile them? So today, I decided to resolve this mystery as a gift for myself.
Linking is part of the c++ compilation process, and the goal of this process is to link object files together to create the executable. Libraries (except header-only libraries) are pre-compiled object files, so there are two things that we want to achieve while using them, get the pre-compiled object files and link them to our projects.
What Linking does
The first thing I did was I figured what linking does. Below is an example.
helper.cpp
int sum(int x, int y) {
return x + y;
}
Let’s create the shared library by g++ -fPIC -c -Wall helper.cpp && ld -shared helper.o -o libhelper.so
. This command compiles helper.cpp
and created the shared library called libhelper.so
.
main.cpp
#include "stdio.h"
int sum(int x, int y);
int main() {
printf("sum %d\n", sum(1, 2));
return 0;
}
Compile it with g++ -Wall -L/home/saber/learn/cpp/linking -Wl,-rpath=/home/saber/learn/cpp/linking main.cpp -lhelper
.
This is the output of the exectuable.
sum 3
One thing that I think very interesting which self-explains what linking does is, we didn’t have the definition of sum
in main.cpp
, we just provided the declaration, and the program worked correctly, as the definition of sum
was linked.
Dynamic Library
Dynamic libraries are shared libraries. As the name implies, dynamic libraries can be shared between multiple applications. The linking happens at run-time, so the executable doesn’t have the library objects. Only a single copy of libraries is loaded into the memory and applications share the same libraries, thus the memory space is saved.
Static Library
As the opposite of dynamic library, static library embed the object files as part of the executable, and each executable has its copy of the library, so the executable is larger and the same library will be loaded into memory whenever a program uses it.
Header-only Library
Header only library is just a header file that can include it in your source file. The header file contains the implementation of the library. The advantage of this approach is the library is very easy to be embedded into the projects. Header-only library is common if the library is small, otherwise, the compilation cost will increase a lot.
Use them with CMake
This was the real question that I had. When I found a library on Github, what are the things that I should be looking at for integrating it into my project?
- Find the compiling options that the project provide
- Things that we want to look like are, is the project supposed to be compiled into a shared library, a static library or a header-only library.
Things are easy if it’s a header-only library, just #include
it. For shared libraries or static libraries, we need to compile it first and then link them into our projects.
Assume this is our project structure
├── helper
│ └── helper.cpp
└── main.cpp
main.cpp
is our program, and helper.cpp
is the library source file.
The first thing we want to do is creating a CMakeLists.txt for the library to compile it into a static library
Let’s create a minimalist CMakeLists.txt
in the helper directory.
# CMake instructions to make the static lib
ADD_LIBRARY( HelperLib STATIC
helper.cpp )
Note that to make it a shared library, all we need to do is to change STATIC
to SHARED
.
Then we need another CMakeLists.txt for the main program. Let’s create one under the root directory as
cmake_minimum_required(VERSION 3.10)
# set the project name
project(test)
add_executable(test main.cpp)
# CMake builds the static library
add_subdirectory(${CMAKE_SOURCE_DIR}/helper)
# Use the static library
TARGET_LINK_LIBRARIES(${PROJECT_NAME} HelperLib)
The above setup allows us to create the binary which has HelperLib
linked.