1.介绍一个cout的源码替代品fmtlib
2.C++高级编程fmtlib & fmt 自定义类型格式化输出
3.fmt的痛与对format设计的思考
介绍一个cout的替代品fmtlib
fmtlib v9版本的代码展现出现代化特性,广泛采用了C++的解析核心功能。本文简要介绍fmt的源码使用方式,突出其相对于std::format的解析强大之处。在编译fmtlib时,源码只需在代码头部添加#define FMT_HEADER_ONLY,解析澳乐乳dha溯源码辨真伪并相应配置包含路径。源码
fmtlib提供了一个替代std::cout的解析print方法,其功能几乎等同于cout,源码但存在一点不便:即使你仅需打印单一变量,解析也必须先提供格式字符串。源码这一设计使得print接口略显复杂。解析
值得称赞的源码是,fmtlib的解析print方法支持打印UTF-8字符,这使得中文、源码日文等非英文字符的海南cdf塑源码输出变得简单、无误。然而,fmtlib并不支持std string lib的字面值类型,这是其目前的局限之一。
对于需要向容器中尾插元素的需求,std::back_inserter提供了解决方案,结合format_to可实现先组合字符串再执行尾插的操作。
fmtlib同样支持stderr的错误信息打印,增强了程序的诊断能力。
{ { }}}这一特殊的语法用于输出大括号,两个大括号起到转义作用,便于在输出中包含大括号字符。
命令行输出颜色和字体样式的能力,通过include fmt/color.h实现。fg和bg函数分别用于设置前景色和背景色,收获区域指标源码通过管道处理实现流式输出。
fmtlib还封装了chrono库和ranges.h库,前者提供了时间相关的功能,后者简化了字符串组合操作。然而,ranges.h的实现较为简略,只能满足基本需求,而chrono库的代码量也相对有限,设计相对粗糙。
综上所述,fmtlib推荐使用其format部分,该部分功能强大、易用。而其他封装的库在实现质量和标准库相比,还有提升空间,电脑端游源码因此建议优先考虑使用标准库提供的相应功能。
C++高级编程fmtlib & fmt 自定义类型格式化输出
定义一个自定义类型的关键在于其格式化输出。在C++中,fmtlib和fmt库提供了一种强大的方式来实现自定义类型格式化输出。
使用fmtlib和fmt库进行自定义类型格式化输出需要实现一个类,并重载其格式化输出相关的函数。例如,创建一个名为KeyVal的类,包含一个字符串成员m_key,可以通过成员函数getKey()获取。
类定义如下:
cpp
class KeyVal { public: const std::string &getKey()const { return m_key;}};
在类中,通过实现getKey()函数来返回m_key值。格式化输出函数通常使用fmt库的format()函数,例如:
cpp
std::string test_format(KeyVal kv1) { return fmt::format("kv2:{ :d}", kv1.getKey());}
然而,尝试将格式化字符串与自定义类型结合时,海泰系统源码可能会遇到一些问题。例如,尝试将kv1对象与字符串格式化输出格式化在一起:
cpp
cout << fmt::format("kv2:{ :d}", kv1) << endl;
编译器会抛出错误:"expression ‘’ is not a constant expression"。这是因为fmt库在编译时进行优化,需要在编译时就确定所有表达式的值。当尝试将自定义类型与格式化输出格式化在一起时,fmt库无法在编译时确定所有表达式的值。
为解决此问题,需要确保在格式化字符串中使用的表达式为常量表达式。对于上面的例子,应该将格式化字符串更改为:
cpp
throw fmt::format_error { "Invalid kv format specifier."};
这将防止尝试在非常量表达式中使用格式化输出,并确保程序在编译时正确运行。
fmt的痛与对format设计的思考
对于iostream中操作符重载的设计,我始终认为它存在不美观和带状态的局限性。相比之下,fmtlib以其类似printf的formatter和data组合,以及编译期的类型检查和Python风格的格式化,成为了我的心头好。然而,fmtlib并非完美无缺,遇到字符类型混用时,它并不支持,甚至有明确的规定来避免这种使用。
尽管如此,如果预先了解源和目标字符集,转换字符类型并非不可能。我曾采取hack,通过编码字符类型信息到字符串长度的高位来处理这种情况。然而,升级到fmt9和C++后,我失去了进一步深入研究和维护的意愿,因为遇到了不满意的细节,于是决定总结一下这些经历。
尽管printf在编译器中有类型检查,但C++引入模板解决了变长参数的问题,确保了函数签名的完整性和类型信息的零开销。fmtlib在实现类型检查时,起初并不支持编译期检查,但后来通过constexpr和模板技术实现了这一功能。
尽管fmtlib在运行期也有类型信息的处理,但它依赖于自定义的枚举来编码,类似于带类型信息的va_list。尽管如此,fmtlib的性能优势可能部分源于对短格式串的优化,但运行时的格式检查和parsing开销仍然不可忽视。
fast_io则选择了一个不同的路径,通过变长函数模板来避免formatter的解析开销。尽管这简化了逻辑,但fmtlib的性能优势可能更多归功于格式化的速度而非parsing。实际上,fmtlib的FMT_COMPILE尝试在编译期解析以减少运行时开销,但也受限于编译期格式串的必要性。
尽管fmtlib在编译期和运行期的检查都有所考虑,但它在动态格式化的场景下,如动态参数构造,会面临性能挑战。动态地狱的问题在于,即使在编译时检查了格式,运行时仍需进行格式匹配,增加了不必要的开销。
对于format设计的思考,或许可以从其他语言的实践中汲取灵感,比如Python和JavaScript的编译器支持,它们提供了一步到位的编译期解析。然而,对于fmtlib来说,字节码编译可能是改进的方向,既能保留格式数据分离,又能减少解析步骤,同时保持灵活性和性能。
尽管如此,我仍期待C++在format设计上能有更好的解决方案,例如,是否能在编译器层面提供更直接的语法支持,或者找到一种平衡,既能保持代码的灵活性,又能避免运行时的重复解析。我的探索还在继续,寻找format设计的最优解。