String formatting like it's C++20 – A tutorial on using {fmt}, Conan, and CMake

Last update: November 9th - updated conan file names, content, and commands.

In the fifth edition of Professional C++, Marc Gregoire uses many new C++20 features in code examples. Unfortunately, some of these features are not supported in GCC 11.1, which I’m currently using on my Linux machine.

I was particularly interested in the std::format function, because it makes the language look a little bit approachable. I personally found the string concatenation syntax in C++ a bit of an eye sore.

At this point though, format and formatter are not supported by any C++ compiler. After looking around a bit I found Marc’s comment under a video of his CppCon’s presentation on that particular feature. Turns out std::format is actually based on the {fmt} library. I figured I could get to use the new syntax, and learn how to import external libraries at the same time by trying it out.

Note and credits

While I wrote this post from scratch, I did follow the tutorial available in Conan’s documentation. Most of the Bash commands come from there. I included minor tweaks here for my specific use case (this is basically a backup for myself before I forget how I got my build to run :D).

Installing and configuring Conan

Conan is a C/C++ package manager. The last time I tried learning C++, I remember finding manual package management and compilation/linking really difficult to grasp. Even on Windows, where tools like Visual Studio make it easier. Fortunately, this time I heard of Conan and decided to use it along CMake to make the building process a bit smoother.

The best way to install Conan is using Python’s pip:

pip install conan

I did this within a virtual environment based on Python 3, but you can also install it for all users, for example using a specific installation of pip. Use your Linux distro’s preferred method of package installation to get it.

sudo pip3 install conan

After installing Conan, you should also configure it:

conan profile new default --detect
conan profile update settings.compiler.libcxx=libstdc++11 default

The first command creates a new, default profile for conan to use on your user account. It detects your compiler configuration to set itself up properly. It will also display a message about what you might need to do if you are using GCC version higher than 5. That’s the second command - it simply ensures that the libraries you download will be compiled using modern C++.

Adding Conan and {fmt} to your project

The next step is to add Conan and the proper libraries to your project. In my case, I was interested in the {fmt} library. This library is available in Conan’s default repository. To search for it and its available versions, use the command below:

conan search fmt --remote=conancenter

This will output appropriate information about the versions that you can add to your project.

Go to your project root, and next to your CMakeLists.txt create a new file: conanfile.txt. Add the following to that file:

[requires]
fmt/8.0.1

[generators]
cmake

You might want to use a different version of {fmt} if it is available - 8.0.1 is current as of this writing.

Next, create the build directory (where all your built assets will go) and switch to it, and then issue the conan install command:

cd build/
conan install .. --build=fmt

Issuing the conan install command will install {fmt} and its dependencies. It will also create a CMake configuration file for you to add to your CMakeLists.txt file. In my case, there was no prebuilt version of fmt for my operating system and architecture, so I added --build=fmt to build fmt from sources.

Configuring CMake and building your project

Open your CMakeLists.txt file and add the lines marked with // <-- below (I include the entire file in case you want to double check your configuration.)

cmake_minimum_required(VERSION 3.21)

project(ProfessionalCPPSandbox VERSION 1.0)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) // <--
conan_basic_setup() // <--

add_executable(ProfessionalCPPSandbox main.cpp)
target_link_libraries(ProfessionalCPPSandbox ${CONAN_LIBS}) // <--

After that, go back to your terminal and in the build/ directory issue the following commands:

cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug
cmake --build .

This will build your project and place the executable file in the /build/bin directory under your project root. In this case, the complete path and file name is /build/bin/ProfessionalCPPSandbox.

This concludes the entire setup. You should now be able to easily rebuild your project by running cmake --build . in your /build directory.

To test it, try the following snippet:

#include <fmt/core.h>
#include <iostream>

using std::cout;

int main() {
  cout << fmt::format("This is a {} example...\n", "formatting");

  return 0;
}