Use Conan: An Open Source C/C++ Package Manager (2024)

Managing your C/C++ packages could be messy, but it can be significantly improved

Use Conan: An Open Source C/C++ Package Manager (3)

In this article, I’ll go through the troubles of package management in C/C++ and how Conan can help you resolve these problems.

Let's start with a trivial task, taking values from the command line arguments and printing them to the standard output:

+root
- mainapp.c
- CMakeLists.txt

We use the getops function from the standard library to get the arguments -a and -b and their associated values, and then we print them.

Then we use CMake to compile the project into an executable called myproject.

Let's configure this project:

cmake --build ./build --config Debug --target MyProject -j 10 --

And now let's run the application:

./build/MyProject -a foo -b bar

The output is:

a: foo
b: bar

It works, but ‘works’ isn’t always enough.
The Getops function uses global variables such as optarg to store the arguments associated value, optopt for the variables name, and optind for the index.

This means that the function is not thread safe, and you can only parse a single input vector in a single thread.

If you have multiple input vectors, you need to parse these vectors sequentially, and to make things worse you have to reset the state of the parser every time.

Another well-known problem of getops is the error message that’s internally hidden which makes it difficult to redirect.

So, to fix this problem, I’ve decided to use parg, an open source C library to parse arguments with thread safety by allocating a struct and using it to monitor the state of the parser instead of global variables.

Option 1. Consume source code

We can use gits submodule tool to clone parg to our project directory and then to use cmake to compile and link it to our executable.

git submodules add https://github.com/jibsen/parg.git

This will create the file .gitsubmodules with the following content:

[submodule "parg"]path = pargurl = https://github.com/jibsen/parg.git

Now we can fetch the repo with its source code:

git submodules init

We have all of parg source code in the parg directory.

Now we need to add this to our CMakeLists.txt configurations:

We’ve added the directory and linked the library to our executable.
Now we can change our code to use the parg library:

Now our code is thread safe, and we can easily parse multiple vectors of arguments if needed.

Let's compile:

cmake --build ./build --config Debug --target MyProject -j 10 --

Run:

./build/MyProject -a foo -b bar

The output is:

a: foo
b: bar

The main limitation of adding the source code is that we need to compile it every time we compile the executable and this can lead to prolonged compilation times.

Option 2 . Consume compiled library

In this option, we will compile parg once, save the library, and then link the library directly without compiling it again.

Having our parg project compiled will create the file libaprg.a.

Now we can make some modifications to the CMakeLists and C code to make it work:

CMake:

And now the C code:

The only change to the C code is that now the header file is placed in the linked library.

In the CMakeLists file, we’ve removed the add_subdirectories and instead, we’ve linked the compiled library from its path so that we are not going to compile it every time we compile the application.

Now let’s compile and run again.

cmake --build ./build --config Debug --target MyProject -j 10 --

Run:

./build/MyProject -a foo -b bar

The output is:

a: foo
b: bar

It works! it’s better, but it can get even better than that!

The problem with this approach is that it contaminates the working environment and when you have many dependencies, and your dependencies have second-tier or even third-tier dependencies, which becomes extremely cumbersome to manage.

Option 3. Use Conan for crying out loud!

Conan is an open source C\C++ package manager developed by JFrog.
It uses the JFrog artifactory to manage dependencies.
It is mostly used by its Software as a Service (SaaS) platform, but it can also be used as a local repository for internal company's needs.

As a prerequisite, you need Python installed.

First, we need to install Conan from pip using the following command:

pip install conan

Now we need to create a profile for our configurations:

conan profile new myprofile

The output will be:

Empty profile created: C:\Users\user\.conan\profiles\myprofile

Now we can go to the profile file and set the configurations as we need.
In my case, I used the following:

[env]CC=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/gcc.exeCXX=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/g++.exe[settings]os_build=Windowsarch_build=x86os=Windowsarch=x86compiler=gcccompiler.version=8compiler.libcxx=libstdc++11build_type=Release

This sets all the paths and variables needed to compile the project.

Having the profile set, now I’ll create conanfile.txt in the project directory.
Looking at the parg repository in Conan center, I can find all the details I need.

[requires]
parg/1.0.2
[generators]
cmake

Now my project is dependent on parg and Conan knows this, so it should be provisioned for cmake.

Now I can run the Conan install command:

conan install . -pr=myprofile

Output:

Configuration:
[settings]
arch=x86
arch_build=x86
build_type=Release
compiler=gcc
compiler.libcxx=libstdc++11
compiler.version=8
os=Windows
os_build=Windows
[options]
[build_requires]
[env]
CC=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/gcc.exe
CXX=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/g++.exe
conanfile.txt: Installing package
Requirements
parg/1.0.2 from 'conancenter' - Cache
Packages
parg/1.0.2:a955db98e980a5ab86ae50d6df8bfee361185c27 - Cache
Installing (downloading, building) binaries...
parg/1.0.2: Already installed!
conanfile.txt: Generator txt created conanbuildinfo.txt
conanfile.txt: Generator cmake created conanbuildinfo.cmake
conanfile.txt: Aggregating env generators
conanfile.txt: Generated conaninfo.txt
conanfile.txt: Generated graphinfo

Cool!

Now let's add some changes to the CMakeLists.txt file so it will take the library with these dependencies:

So we included the build info from the sources dir (it was created by the Conan install) and linked the library.

Now we can use the same C code we’ve used in the first example, as shown below:

The difference is that now we don’t pollute our code with git submodules and we won’t have to compile the code every time.
Likewise, we don’t need to manage dependencies and artifacts on our own anymore.

Now let's compile and run this code again:

cmake --build ./build --config Debug --target MyProject -j 10 --

Run:

./build/MyProject -a foo -b bar

The output is:

a: foo
b: bar

Sweet!

Use Conan: An Open Source C/C++ Package Manager (2024)

References

Top Articles
Latest Posts
Article information

Author: Horacio Brakus JD

Last Updated:

Views: 5927

Rating: 4 / 5 (71 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Horacio Brakus JD

Birthday: 1999-08-21

Address: Apt. 524 43384 Minnie Prairie, South Edda, MA 62804

Phone: +5931039998219

Job: Sales Strategist

Hobby: Sculling, Kitesurfing, Orienteering, Painting, Computer programming, Creative writing, Scuba diving

Introduction: My name is Horacio Brakus JD, I am a lively, splendid, jolly, vivacious, vast, cheerful, agreeable person who loves writing and wants to share my knowledge and understanding with you.