Введение в библиотеку fmt для C++
Библиотека fmt (или fmtlib) — это современная, быстрая и безопасная библиотека для форматирования строк в C++. Она предоставляет альтернативу устаревшим функциям вроде printf и sprintf, а также громоздким потокам std::stringstream. Основная цель fmt — сделать форматирование строк простым, типобезопасным, расширяемым и производительным.
Зачем нужна fmt? Во-первых, она решает проблему безопасности типов. printf не проверяет типы аргументов, что приводит к неопределённому поведению и трудноуловимым багам. fmt же использует шаблоны C++ и проверяет типы на этапе компиляции. Во-вторых, fmt значительно быстрее, чем std::stringstream, и часто быстрее, чем printf. В-третьих, её синтаксис интуитивно понятен и напоминает форматирование в Python, что упрощает переход между языками. Настолько, что fmt стала основой для std::format, который был принят в стандарт C++20.
На этом сайте pythonlib.ru мы часто говорим о Python, но fmt — это отличный пример того, как хорошие идеи из Python перекочевали в мир C++ и стали там стандартом де-факто. Если вы пишете на C++ и хотите делать это современно, fmt — обязательная библиотека в вашем арсенале.
Установка библиотеки fmt
Установка через пакетные менеджеры (Conan, vcpkg)
Самый простой способ установить fmt в современный C++ проект — использовать менеджер пакетов.
Conan:
conan install fmt/10.1.1@
# или в вашем conanfile.txt:
# [requires]
# fmt/10.1.1
vcpkg:
vcpkg install fmt
# Затем в CMakeLists.txt:
# find_package(fmt CONFIG REQUIRED)
# target_link_libraries(your_project PRIVATE fmt::fmt)
Установка вручную (сборка из исходников)
Вы можете скачать исходный код с GitHub и собрать его с помощью CMake:
git clone https://github.com/fmtlib/fmt.git
cd fmt
mkdir build && cd build
cmake ..
make -j4
sudo make install
Установка через pip (для использования в Python-расширениях)
Хотя fmt — это C++ библиотека, её можно установить как Python-пакет, если вы пишете расширение на C++ с использованием pybind11 или cppyy:
pip install fmt
Однако, в контексте чистого C++ это не имеет смысла. Обычно вы просто подключаете заголовочные файлы.
Подключение к проекту (header-only режим)
fmt можно использовать в header-only режиме, просто скопировав папку include/fmt в ваш проект и добавив:
#define FMT_HEADER_ONLY
#include "fmt/core.h"
#include "fmt/format.h"
Быстрый старт с примером кода
Давайте начнём с самого простого примера, который демонстрирует базовое форматирование:
#include <fmt/core.h>
#include <fmt/format.h>
#include <iostream>
int main() {
// Простейшее форматирование
std::string message = fmt::format("Привет, {}! Тебе {} лет.", "Мир", 42);
std::cout << message << std::endl;
// Вывод в stdout
fmt::print("Число Пи примерно равно {:.2f}\n", 3.14159);
// Форматирование с именованными аргументами (C++20 стиль)
fmt::print("Пользователь: {name}, Возраст: {age}\n",
fmt::arg("name", "Алиса"),
fmt::arg("age", 30));
return 0;
}
Этот код выведет:
Привет, Мир! Тебе 42 лет.
Число Пи примерно равно 3.14
Пользователь: Алиса, Возраст: 30
Как видите, синтаксис {} очень похож на Python. Всё интуитивно и читаемо.
Основные методы и функции библиотеки fmt
Ниже приведён подробный разбор ключевых функций fmt. Мы рассмотрим их сигнатуры, параметры и примеры использования.
1. fmt::format
Описание: Основная функция для форматирования строки. Возвращает std::string.
Параметры:
format_string— строка формата (должна быть константой времени компиляции для проверки типов).args...— аргументы для форматирования (переменное количество).
Пример:
#include <fmt/format.h>
int main() {
std::string result = fmt::format("{} + {} = {}", 2, 3, 2 + 3);
// result = "2 + 3 = 5"
fmt::print("Результат: {}\n", result);
return 0;
}
2. fmt::print
Описание: Форматирует строку и сразу выводит её в стандартный поток вывода (stdout). Аналог printf, но типобезопасный.
Параметры:
format_string— строка формата.args...— аргументы для форматирования.
Пример:
#include <fmt/core.h>
int main() {
fmt::print("Значение: {}\n", 42);
fmt::print("Шестнадцатеричное: {:X}\n", 255); // Выведет FF
return 0;
}
3. fmt::format_to
Описание: Форматирует строку и записывает результат в указанный итератор вывода (например, в std::back_inserter).
Параметры:
out— итератор вывода.format_string— строка формата.args...— аргументы.
Пример:
#include <fmt/format.h>
#include <vector>
int main() {
std::vector<char> buf;
fmt::format_to(std::back_inserter(buf), "Число: {}", 123);
// buf содержит "Число: 123"
fmt::print("{}\n", std::string(buf.begin(), buf.end()));
return 0;
}
4. fmt::format_to_n
Описание: Форматирует строку, но не более чем на n символов. Возвращает результат типа fmt::format_to_n_result, который содержит итератор и количество записанных символов.
Параметры:
out— итератор вывода.n— максимальное количество символов для записи.format_string— строка формата.args...— аргументы.
Пример:
#include <fmt/format.h>
#include <array>
int main() {
std::array<char, 10> buf = {};
auto result = fmt::format_to_n(buf.begin(), 9, "Длинная строка для обрезки");
*result.out = '\0'; // Добавляем нуль-терминатор
fmt::print("Обрезанная строка: '{}'\n", buf.data());
return 0;
}
5. fmt::formatted_size
Описание: Вычисляет количество символов, которое потребуется для форматирования строки, без фактического форматирования. Полезно для предварительного выделения буфера.
Параметры:
format_string— строка формата.args...— аргументы.
Пример:
#include <fmt/format.h>
int main() {
auto size = fmt::formatted_size("{} + {} = {}", 10, 20, 30);
fmt::print("Необходимый размер буфера: {}\n", size); // Выведет 12
return 0;
}
6. fmt::join
Описание: Объединяет элементы контейнера (или диапазона) в строку, разделяя их указанным разделителем. Возвращает объект, который можно передать в fmt::format или fmt::print.
Параметры:
range— контейнер или диапазон (должен поддерживать итерацию).delimiter— строка-разделитель.
Пример:
#include <fmt/ranges.h>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
fmt::print("Вектор: [{}]\n", fmt::join(vec, ", "));
// Вывод: Вектор: [1, 2, 3, 4, 5]
return 0;
}
7. fmt::arg
Описание: Создаёт именованный аргумент для форматирования. Позволяет использовать имена вместо позиционных индексов.
Параметры:
name— строка с именем аргумента.value— значение аргумента.
Пример:
#include <fmt/core.h>
int main() {
fmt::print("Привет, {name}! Твой ID: {id}\n",
fmt::arg("name", "Боб"),
fmt::arg("id", 007));
// Вывод: Привет, Боб! Твой ID: 7
return 0;
}
8. fmt::ptr
Описание: Форматирует указатель в читаемый вид (обычно в шестнадцатеричном формате с префиксом 0x).
Параметры:
ptr— указатель любого типа.
Пример:
#include <fmt/core.h>
int main() {
int x = 42;
fmt::print("Адрес переменной x: {}\n", fmt::ptr(&x));
// Пример вывода: Адрес переменной x: 0x7ffd5e8b3a4c
return 0;
}
9. fmt::dynamic_format_arg_store
Описание: Позволяет динамически собирать аргументы для форматирования во время выполнения. Полезно, когда набор аргументов неизвестен на этапе компиляции.
Параметры:
- Метод
push_backдля добавления аргументов. - Метод
formatдля выполнения форматирования.
Пример:
#include <fmt/format.h>
int main() {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("name", "Динамика"));
store.push_back(fmt::arg("value", 99));
std::string result = fmt::vformat("Привет