There is spectrum on how easy it is to integrate a C (or C++ for that matter) library into a project.
The issue is that the C ecosystem does not have a standard dependency manager, so each project big enough to require the use of external libraries needs to find a way to integrate them, and it is usually a significant source of problem just to get them to compile. Note that I am not even talking about how to use them.
This is particularly annoying for mobile phone applications running on android or iOS, because most of the time the build system used by libraries are targeted at desktop usage and just won’t work with android studio or xcode.
So here are my circles of hell of integrating a library:
In the first circle (in the Dante sense of being the less painful), are the “single file libraries”, as championed by Sean Barrett, author of the famous stb libraries
The idea is that each library is distributed as a single header that contains both the function declaration and the implementation. You use it like that:
#define STB_IMAGE_IMPLEMENTATION // Put this only in one file. #define STBI_ONLY_PNG // Optional configuration of the lib. #include "stb_image.h"
Now, you just need to make sure the file
stb_image.h is in your include
search paths list, which is usually not a problem for a single file.
Because of this simplicity, I usually look for a single file library first when I need anything.
In the second circles are the libraries that are split into multiple header and source, files but specifically designed to be dropped as it is in the project.
Examples of such a library are nanovg, or Dear ImGui. This is slightly more complicated and you basically have two ways to integrate those kind of libraries:
- Add all the source files along your code, and set the needed defines as argument in your build system. So for example to use imgui in a project compiled with a Makefile you would add something like that:
SRC += $(wildcard third_party/imgui/*.cpp) FLAGS += -Ithird_party/imgui
- Another option that I like, though it is a bit of a hack, is to create
a source file from which I include all the source files of the library.
This is convenient because I can also add the
#defineneeded, and silence the eventual compilation error. For example here is how I compile imgui in one of my project:
- Another option that I like, though it is a bit of a hack, is to create a source file from which I include all the source files of the library. This is convenient because I can also add the
#define GL_GLEXT_PROTOTYPES #define GL_SILENCE_DEPRECATION #define IMGUI_IMPL_OPENGL_LOADER_CUSTOM #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include "../third_party/imgui/imgui.cpp" #include "../third_party/imgui/imgui_draw.cpp" #include "../third_party/imgui/imgui_widgets.cpp" #include "../third_party/imgui/imgui_tables.cpp" #include "../third_party/imgui/imgui_impl_glfw.cpp" #include "../third_party/imgui/imgui_impl_opengl3.cpp"
The big cavea here is that this only works if the library source does not reuse names for static variables.
Here I have to give high commendation to the SQlite library for actually providing an already merged source code version of each release (the sqlite amalgamation).
The next circle of integration complexity are libraries that are possible to directly past into your source code tree, but not as a first class solution. The official way to use those libraries is to first compile them as a static (.a) or dynamic (.so or .dll) binary for your target platform using the provided build system.
Those are fine (and indeed required) if you actually want to use a dynamic library. A linux distribution will need such a build system for example.
To integrate those libraries, the choices are:
- Pre-compile the library and bundle the binary into your project. This works, but makes it difficult to make your code cross platform.
- What I usually try: manually extract the required headers and source files and add those into your project. The issue is that since this is not the recommended way, there are often a lot of little issues, such as:
- The library using multiple directories for headers, that all need to be added to your include path separately (and hoping no name clashes with your own headers).
- The library having some sort of ‘config.h’ file generated by the build system.
- The library using a lot of defines set by the build system.
Finally, in the last circle of hell are the libraries that are basically impossible to integrate into a different build system, usually because they themself use too many dependencies, and rely too much on the build system.
For those, the only way is to independently compile them for your target, which can by itself be a difficult task.