Some Notes About Linking in C++

[linkstandalone]

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?

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.