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;
}