当前位置: 首页 > news >正文

C++之fmt库介绍和使用(2)

C++之fmt库介绍与使用(2)


Author: Once Day Date: 2025年5月19日

一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…

漫漫长路,有人对你微笑过嘛…

全系列文章可参考专栏: 源码分析_Once-Day的博客-CSDN博客

参考文章:

  • Get Started - {fmt}
  • fmtlib/fmt: A modern formatting library

文章目录

  • C++之fmt库介绍与使用(2)
        • 3. 格式化API
          • 3.1 实用工具函数
          • 3.2 系统错误
          • 3.3 自定义分配器
          • 3.4 区域设置(Locale)
          • 3.5 旧版编译时检查
        • 4. 其他特性
          • 4.1 范围和元组格式化
          • 4.2 日期和时间格式化
          • 4.3 终端颜色和文本样式
          • 4.4 动态参数列表

3. 格式化API

fmt/format.h 定义了完整的格式化 API,提供额外的格式化函数和区域设置支持。

template <typename... T>
auto format(format_string<T...> fmt, T&&... args) -> std::string;#include <fmt/format.h>
std::string message = fmt::format("The answer is {}.", 42);

根据 fmt 中的规范格式化参数 args,并将结果作为字符串返回。

auto vformat(string_view fmt, format_args args) -⁠> std::string;template <detail::fixed_string S>
constexpr auto operator""_a();
3.1 实用工具函数
template <typename T>
auto ptr(T p) -> const void*;auto s = fmt::format("{}", fmt::ptr(p));

将指针 p 转换为 const void*,用于指针格式化。

template <typename Enum>
constexpr auto underlying(Enum e) -⁠> underlying_t<Enum>;enum class color { red, green, blue };
auto s = fmt::format("{}", fmt::underlying(color::red));  // s == "0"

将枚举值 e 转换为其底层类型。

template <typename T>
auto to_string(const T& value) -⁠> std::string;

将任意对象转换为字符串std::string对象。

template <typename T>
auto group_digits(T value) -⁠> group_digits_view<T>;fmt::print("{}", fmt::group_digits(12345));
// Output: "12,345"

返回一个视图,使用与区域设置无关的逗号 , 作为千位分隔符格式化整数值。

template <typename T>
class detail::buffer;constexpr auto size() -⁠> size_t; //返回缓冲区的大小。
constexpr auto capacity() -⁠> size_t; // 返回缓冲区的容量。
auto data() -> T*; // 返回指向缓冲区数据的指针(无空终止符)。
void clear();	   // 清空缓冲区。
void append(const U* begin, const U* end); // 将数据追加到缓冲区末尾。

具有可选增长能力的连续内存缓冲区。它是内部类,不应直接使用,需通过 memory_buffer 使用。

template <typename T, size_t SIZE, typename Allocator>
class basic_memory_buffer;// 这会将 "The answer is 42." 追加到 out 中。缓冲区内容可通过 to_string(out) 转换为 std::string。
auto out = fmt::memory_buffer();
fmt::format_to(std::back_inserter(out), "The answer is {}.", 42);// 通过移动构造函数从另一个对象转移内容。
basic_memory_buffer(basic_memory_buffer&& other);// 通过移动赋值运算符从另一个对象转移内容。
auto operator=(basic_memory_buffer&& other) -> basic_memory_buffer&;// 调整缓冲区大小以容纳 count 个元素。如果 T 是 POD 类型,新元素可能未初始化。
void resize(size_t count);// 将缓冲区容量增加到 new_capacity。
void reserve(size_t new_capacity);

为平凡可复制 / 构造的类型提供的动态增长内存缓冲区,前 SIZE 个元素存储在对象自身中。最常见的用法是通过 char 类型的 memory_buffer 别名。

3.2 系统错误

{fmt} 库不使用 errno 向用户传递错误信息,但可能会调用设置 errno 的系统函数。用户不应假设库函数会保留 errno 的值。

template <typename... T>
auto system_error(int error_code, format_string<T...> fmt, T&&... args) -> std::system_error;// 抛出 std::system_error,错误描述为 "cannot open file 'madeup': No such file or directory"(系统消息可能因环境而异)。
const char* filename = "madeup";
FILE* file = fopen(filename, "r");
if (!file)throw fmt::system_error(errno, "cannot open file '{}'", filename);

使用 fmt::format(fmt, args...) 格式化错误消息,构造 std::system_error 对象。error_codeerrno 提供的系统错误码。

void format_system_error(detail::buffer<char>& out, int error_code, const char* message);

为操作系统或语言运行时返回的错误(如文件打开错误)格式化错误消息,并写入 out。格式与 std::system_error(ec, message) 一致(其中 ecstd::error_code(error_code, std::generic_category()))。实现可能不同,但通常格式为:

<message>: <system-message>

其中 <message> 是传入的消息,<system-message> 是与错误码对应的系统消息。error_codeerrno 提供的系统错误码。

3.3 自定义分配器

{fmt} 库支持自定义动态内存分配器。自定义分配器类可作为模板参数指定给 fmt::basic_memory_buffer

using custom_memory_buffer = fmt::basic_memory_buffer<char, fmt::inline_buffer_size, custom_allocator>;

也可以编写使用自定义分配器的格式化函数:

using custom_string =std::basic_string<char, std::char_traits<char>, custom_allocator>;auto vformat(custom_allocator alloc, fmt::string_view fmt,fmt::format_args args) -> custom_string {auto buf = custom_memory_buffer(alloc);fmt::vformat_to(std::back_inserter(buf), fmt, args);return custom_string(buf.data(), buf.size(), alloc);
}template <typename ...Args>
auto format(custom_allocator alloc, fmt::string_view fmt,const Args& ... args) -> custom_string {return vformat(alloc, fmt, fmt::make_format_args(args...));
}

分配器仅用于输出容器。格式化函数通常不会为内置类型和字符串类型执行任何分配操作,除非是非默认的浮点格式化(偶尔会回退到 sprintf)。

3.4 区域设置(Locale)

默认情况下,所有格式化操作均与区域设置无关。使用 'L' 格式说明符可插入区域设置中的合适数字分隔符:

#include <fmt/core.h>
#include <locale>std::locale::global(std::locale("en_US.UTF-8"));
auto s = fmt::format("{:L}", 1000000);  // s == "1,000,000"

fmt/format.h 提供以下接受 std::locale 作为参数的格式化函数重载。locale 类型作为模板参数,避免引入开销较大的 <locale> 头文件:

template <typename... T>
auto format(detail::locale_ref loc, format_string<T...> fmt, T&&... args) -> std::string;template <typename OutputIt, typename... T>
auto format_to(OutputIt out, detail::locale_ref loc, format_string<T...> fmt, T&&... args) -> OutputIt;template <typename... T>
auto formatted_size(detail::locale_ref loc, format_string<T...> fmt, T&&... args) -> size_t;
3.5 旧版编译时检查

FMT_STRING 宏用于在旧版编译器上启用编译时检查,要求 C++14 或更高版本,在 C++11 中为无操作(no-op)。

FMT_STRING(s)
// 编译时错误:'d' 是字符串的无效说明符
std::string s = fmt::format(FMT_STRING("{:d}"), "foo");

从字符串字面量 s 构造旧版编译时格式字符串。

若要强制使用旧版编译时检查,定义预处理变量 FMT_ENFORCE_COMPILE_STRING。设置后,接受 FMT_STRING 的函数将无法使用常规字符串编译。

4. 其他特性
4.1 范围和元组格式化

fmt/ranges.h 为范围(ranges)和元组(tuples)提供格式化支持:

#include <fmt/ranges.h>fmt::print("{}", std::tuple<char, int>{'a', 42});
// 输出:('a', 42)

通过 fmt::join,可以使用自定义分隔符分隔元组元素:

#include <fmt/ranges.h>auto t = std::tuple<int, char>{1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// 输出:1, a
template <typename Range>
auto join(Range&& r, string_view sep) -> join_view<decltype(detail::range_begin(r)), decltype(detail::range_end(r))>;auto v = std::vector<int>{1, 2, 3};
fmt::print("{}", fmt::join(v, ", "));
// 输出:1, 2, 3// fmt::join 会将传递的格式说明符应用于范围元素:
fmt::print("{:02}", fmt::join(v, ", ")); 
// 输出:01, 02, 03

返回一个视图,使用 sep 分隔范围元素进行格式化。

template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>;

返回一个视图,格式化迭代器范围 [begin, end),元素用 sep 分隔。

template <typename T>
auto join(std::initializer_list<T> list, string_view sep) -> join_view<const T*, const T*>;// 输出:"1, 2, 3"
fmt::print("{}", fmt::join({1, 2, 3}, ", "));

返回一个对象,格式化 std::initializer_list,元素用 sep 分隔。

4.2 日期和时间格式化

fmt/chrono.h 为以下类型提供格式化器:

  • std::chrono::duration
  • std::chrono::time_point
  • std::tm

格式语法请参考 Chrono 格式规范。

#include <fmt/chrono.h>int main() {std::time_t t = std::time(nullptr);fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));// 输出:The date is 2020-11-07.// (2020-11-07 为当前日期)using namespace std::literals::chrono_literals;fmt::print("Default format: {} {}\n", 42s, 100ms);// 输出:Default format: 42s 100msfmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);// 输出:strftime-like format: 03:15:30
}

std::time_t 表示的自 epoch 以来的时间转换为本地时间的日历时间(std::tm)。与 std::localtime 不同,该函数在大多数平台上是线程安全的。

auto localtime(std::time_t time) -> std::tm;

std::time_t 表示的自 epoch 以来的时间转换为协调世界时(UTC)的日历时间(std::tm)。与 std::gmtime 不同,该函数在大多数平台上是线程安全的。

auto gmtime(std::time_t time) -> std::tm;
4.3 终端颜色和文本样式

fmt/color.h 提供终端颜色和文本样式输出支持。

template <typename... T>
void print(const text_style& ts, format_string<T...> fmt, T&&... args);fmt::print(fmt::emphasis::bold | fg(fmt::color::red),"Elapsed time: {0:.2f} seconds", 1.23);

使用 ANSI 转义序列指定文本格式,格式化字符串并打印到标准输出(stdout)。

auto fg(detail::color_type foreground) -> text_style;

从前景色(文本颜色)创建文本样式。

auto bg(detail::color_type background) -⁠> text_style;

从背景色创建文本样式。

template <typename T>
auto styled(const T& value, text_style ts) -> detail::styled_arg<remove_cvref_t<T>>;

返回一个使用 ANSI 转义序列格式化的参数,用于格式化函数。

fmt::print("Elapsed time: {0:.2f} seconds",fmt::styled(1.23, fmt::fg(fmt::color::green) |fmt::bg(fmt::color::blue)));
4.4 动态参数列表

头文件 fmt/args.h 提供了 dynamic_format_arg_store,这是一种类似构建器的 API,用于动态构造格式化参数列表。

template <typename Context>
class dynamic_format_arg_store;

功能:带存储功能的动态格式化参数列表,可隐式转换为 fmt::basic_format_args,用于传递给类型擦除的格式化函数(如 fmt::vformat)。

void push_back(const T& arg);fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);          // 整数
store.push_back("abc");        // 字符串(复制存储)
store.push_back(1.5f);         // 浮点数
std::string result = fmt::vformat("{} and {} and {}", store);
// 输出:"42 and abc and 1.5"

作用:向动态存储中添加参数,供后续格式化函数使用。

注意:自定义类型和字符串类型(非字符串视图)会被复制到存储中,必要时动态分配内存。

void push_back(std::reference_wrapper<T> arg);fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band)); // 存储引用
band[9] = 'c';                    // 修改原始数组
std::string result = fmt::vformat("{}", store);
// 输出:"Rolling Scones"(引用值随原始数据变化)

作用:向动态存储中添加参数的引用(避免复制)。

void push_back(const detail::named_arg<char_type, T>& arg);

作用:添加命名参数(支持 std::reference_wrapper 避免参数复制,但名称始终会被复制到存储中)。

void clear();

作用:清空存储中的所有参数。

void reserve(size_t new_cap, size_t new_cap_named);

作用:预留存储空间,至少容纳 new_cap 个参数(包括 new_cap_named 个命名参数)。

size_t size();

作用:返回存储中的参数数量。

http://www.xdnf.cn/news/548893.html

相关文章:

  • GPS模块_亿佰特E108-GN04D_u-center2调试
  • Linux:面试题
  • CAU数据库class3 关系型数据库基础
  • WebSocket心跳机制
  • 85.评论日记
  • 【C++算法】69.栈_验证栈序列
  • C++类与对象--7 特性三:多态
  • # YOLOv5:目标检测的新里程碑
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(25):受身形(3)
  • GitHub 自动认证教程
  • cv2.VideoWriter_fourcc(*‘mp4v‘)生成的视频无法在浏览器展
  • Fast Video Cutter Joiner v6.8.2 视频剪切合并器汉化版
  • 第10天-Python操作MySQL数据库全攻略:从基础连接到高级应用
  • 云计算与大数据进阶 | 27、存储系统如何突破容量天花板?可扩展架构的核心技术与实践—— 分布式、弹性扩展、高可用的底层逻辑(上)
  • 第7天-Python+PyEcharts实现股票分时图实战教程
  • workflow:高效的流式工作架构
  • BPMN.js编辑器设计器与属性面板数据交互
  • 【动手学深度学习】系列
  • 【AI News | 20250520】每日AI进展
  • 5.20 note
  • 什么是 AI 人工智能?什么是机器学习?什么是深度学习?三者啥关系
  • 基于AutoDL市场下的Pycharm远程控制
  • Redis从入门到实战 - 高级篇(中)
  • Jedis快速入门【springboot】
  • NMOS和PMOS的区别
  • 大语言模型 14 - Manus 超强智能体 开源版本 OpenManus 上手指南
  • 从混乱到高效:我们是如何重构 iOS 上架流程的(含 Appuploader实践)
  • 南柯电子|储能EMC整改:从单点整改到智能预测的进化路径
  • 瑞萨单片机笔记
  • #渗透测试#批量漏洞挖掘#LiveBos UploadFile(CVE-2021-77663-2336) 任意文件上传漏洞