Folly-Format 7.1.3

Folly-Format 7.1.3

Pritesh NandgaonkarLorenzo Blasa维护。



  • 作者:
  • Pritesh Nandgaonkar

{fmt}

https://travis-ci.org/fmtlib/fmt.png?branch=master https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v fmt is continuously fuzzed at oss-fuzz Ask questions at StackOverflow with the tag fmt

{fmt}是一个开源格式化库,提供了快速且安全的C stdio和C++ iostreams的替代方案。

如果您喜欢这个项目,请考虑向BYSOL捐赠以帮助白俄罗斯的政治压迫受害者:[捐赠链接](https://www.facebook.com/donate/759400044849707/108388587646909/)

文档

Q&A:在带有标签fmt的[StackOverflow](https://stackoverflow.com/questions/tagged/fmt)上提问。

在[编译器探索器](https://godbolt.org/z/Eq5763)中试用{fmt}。

特性

  • 简单的格式API,具有位置参数以进行本地化
  • 实现了[cppreference.com](https://cppreference.cn/w/cpp/utility/format)的C++20 std::format
  • 格式字符串语法类似于Python的[format](https://docs.pythonlang.cn/3/library/stdtypes.html#str.format)
  • 快速IEEE 754浮点数格式化器,具有正确的舍入、短精度和往返保证
  • 安全的[printf实现](https://fmt.dev/latest/api.html#printf-formatting),包括对位置参数的POSIX扩展
  • 可扩展性:[支持用户自定义类型](https://fmt.dev/latest/api.html#formatting-user-defined-types)
  • 高性能:比常见的标准库实现更快,包括(s)printf、iostreams、to_string和to_chars,请参阅[速度测试](#speed-tests)和[将一亿个整数转换为字符串每秒](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html)
  • 代码大小小:在最小配置下只包含三个文件,core.h、format.h和format-inl.h,以及编译后的代码;请参阅[编译时间和代码膨胀](#compile-time-and-code-bloat)
  • 可靠性:该库有一系列广泛的[测试](https://github.com/fmtlib/fmt/tree/master/test)并被[持续模糊测试](https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1)
  • 安全性:该库完全类型安全,格式字符串中的错误可以编译时报告,自动内存管理防止缓冲区溢出错误
  • 易用性:小型自包含代码库,无外部依赖,采用宽容的MIT授权协议
  • 兼容性良好,跨平台输出一致,并支持老旧的编译器
  • 干净的代码库,无警告,即使在高级警告级别如-Wall -Wextra -pedantic下也是如此
  • 默认情况下具有地区独立性
  • 可以使用FMT_HEADER_ONLY宏选择性地启用仅头文件配置

想要获取更多详细信息,请参阅文档

示例

输出到标准输出 (运行)

#include <fmt/core.h>

int main() {
  fmt::print("Hello, world!\n");
}

格式化字符串 (运行)

std::string s = fmt::format("The answer is {}.", 42);
// s == "The answer is 42."

使用位置参数格式化字符串 (运行)

std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right."

打印chrono持续时间 (运行)

#include <fmt/chrono.h>

int main() {
  using namespace std::literals::chrono_literals;
  fmt::print("Default format: {} {}\n", 42s, 100ms);
  fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
}

输出

Default format: 42s 100ms
strftime-like format: 03:15:30

打印容器 (运行)

#include <vector>
#include <fmt/ranges.h>

int main() {
  std::vector<int> v = {1, 2, 3};
  fmt::print("{}\n", v);
}

输出

{1, 2, 3}

在编译时检查格式字符串

std::string s = fmt::format(FMT_STRING("{:d}"), "don't panic");

这将引发编译时错误,因为d不是一个适用于字符串的无效格式说明符。

从单个线程写入文件

#include <fmt/os.h>

int main() {
  auto out = fmt::output_file("guide.txt");
  out.print("Don't {}", "Panic");
}

这比fprintf快5到9倍。(参考

用颜色和文本样式打印

#include <fmt/color.h>

int main() {
  fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
             "Hello, {}!\n", "world");
  fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
             fmt::emphasis::underline, "Hello, {}!\n", "мир");
  fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
             "Hello, {}!\n", "世界");
}

在现代终端上的输出

https://user-images.githubusercontent.com/576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png

基准测试

速度测试

方法 运行时间,秒
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23

{fmt}是所有基准测试方法中最快的,比printf快约35%。

上述结果由在macOS 10.14.6上使用clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT构建的tinyformat_test.cpp生成,取了三次运行中最好的一次。在测试中,格式字符串"%0.10f:%04d:%+g:%s:%p:%c:%%\n"或等效字符串填充了2000000次,输出发送到/dev/null;更多详细资料请参阅源代码

{fmt}在浮点格式化方面比std::ostringstreamsprintf快20到30倍,比double-conversionryu快。

https://user-images.githubusercontent.com/576385/95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png

编译时间和代码膨胀

来自 format-benchmark 的脚本 bloat-test.py 测试了非平凡项目的编译时间和代码膨胀。它生成了 100 个翻译单位,并在每个中使用了五次 printf() 或其替代方案来模拟一个中等规模的项目。以下表格显示了生成的可执行文件大小和编译时间(Apple LLVM 版本 8.1.0(clang-802.0.42),macOS Sierra,三次最佳)。

优化构建(-O3)

方法 编译时间,s 可执行文件大小,KiB strip 后大小,KiB
printf 2.6 29 26
printf+字符串 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
Boost Format 91.9 226 203
Folly Format 115.7 101 88

如您所见,与 iostreams 相比,{fmt} 在输出二进制代码大小方面的开销减少60%,并且近乎接近 printf。Boost Format 和 Folly Format 的开销最大。

printf+stringprintf 相同,但增加了额外的 <string> 包含以测量后者的开销。

非优化构建

方法 编译时间,s 可执行文件大小,KiB strip 后大小,KiB
printf 2.2 33 30
printf+字符串 16.0 33 30
iostreams 28.3 56 52
{fmt} 18.2 59 50
Boost Format 54.1 365 303
Folly Format 79.9 445 430

libclib(std)c++libfmt 均作为共享库链接,以比较格式化函数的开销。Boost Format 是一个头文件只库,因此它不提供任何链接选项。

运行测试

有关如何构建库和运行单元测试的说明,请参阅 构建库

基准测试位于单独的存储库中,即 format-benchmarks,因此要运行基准测试,您首先需要克隆此存储库并使用 CMake 生成 Makefiles。

$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .

然后您可以运行速度测试

$ make speed-test

或膨胀测试

$ make bloat-test

使用此库的项目

  • 0 A.D.:一款免费、开源、跨平台的实时战略游戏
  • AMPL/MP:一个开源的数学规划库
  • Aseprite:动画精灵编辑器和像素画工具
  • AvioBook:一个全面的飞机操作套件
  • Blizzard Battle.net:一个在线游戏平台
  • Celestia:实时三维空间可视化
  • Ceph:一个可扩展的分布式存储系统
  • ccache:编译器缓存
  • ClickHouse:分析型数据库管理系统
  • CUAUV:康奈尔大学自主水下航行器
  • Drake:非线性动力学系统规划、控制和分析的工具箱(麻省理工学院)
  • Envoy:C++ L7代理和通信总线(Lyft)
  • FiveM:GTA V修改框架
  • Folly:Facebook开源库
  • HarpyWar/pvpgn:带有调整的玩家对战游戏网络
  • KBEngine:开源MMOG服务器引擎
  • Keypirinha:Windows语义启动器
  • Kodi(原名xbmc):家用影院软件
  • Knuth:高性能比特币全节点
  • Microsoft Verona:并发所有权的研发编程语言
  • MongoDB:分布式文档数据库
  • MongoDB Smasher:生成随机数据集的小工具
  • OpenSpace:开源天体可视化框架
  • PenUltima Online (POL):兼容大多数Ultima Online客户端的MMO服务器
  • PyTorch:开源机器学习库
  • quasardb:分布式、高性能的关联数据库
  • Quill:异步低延迟日志库
  • QKW:将别名泛化以简化导航,并执行复杂的多行终端命令序列
  • redis-cerberus:Redis集群代理
  • redpanda:C++编写的10倍于Kafka®的替代品,适用于关键任务系统
  • rpclib:现代C++ msgpack-RPC服务器和客户端库
  • Salesforce Analytics Cloud:商业智能软件
  • Scylla:兼容Cassandra的NoSQL数据存储,单服务器每秒可处理100万笔交易
  • Seastar:适用于现代硬件的先进开源C++框架,用于高性能服务器应用程序
  • spdlog:超快的C++日志库
  • Stellar:金融平台
  • Touch Surgery:手术模拟器
  • TrinityCore:开源MMORPG框架
  • Windows Terminal:新的Windows终端

更多...

如果您知道使用此库的其他项目,请通过电子邮件或通过提交问题告知我。

动机

为什么还需要另一个格式化库呢?

完成这项任务有许多方法,从标准的方法如printf函数族和iostreams到Boost Format和FastFormat库。创建新库的原因是,我找到的每个现有解决方案要么有严重问题,要么没有提供我需要的所有功能。

printf

关于printf的好处是,它非常快,并且作为C标准库的一部分很容易使用。主要的缺点是它不支持用户定义的数据类型。printf也存在安全问题,尽管GCC中的__attribute__ ((format (printf, ...))) (见https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html)在一定程度上缓解了这些问题。POSIX扩展向printf添加了i18n所需的位置参数,但它不是C99的一部分,可能在一些平台上不可用。

iostreams

iostreams的主要问题最好通过一个例子来说明

std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";

相比printf来说,输入输出流在键盘输入时的打字要多很多。

printf("%.2f\n", 1.23456);

FastFormat的作者Matthew Wilson将其称为“箭头地狱”。iostreams按设计不支持位置参数。

好的一面是iostreams支持用户定义的类型,虽然是安全的,但错误处理有些笨拙。

Boost Format

这是一个非常强大的库,它支持类似于printf的格式字符串和位置参数。它主要的缺点是性能。根据各种基准测试,它的速度比这里考虑的其他方法慢得多。Boost Format还存在着过长的构建时间和严重的代码膨胀问题(请参阅)。

FastFormat

这是一个有趣的库,它快速、安全并支持位置参数。然而,它的局限性很大,如它的作者所提及的

无法适应当前设计的三项特性包括

  • 前导零(或任何其他非空格填充)
  • 八进制/十六进制编码
  • 运行时宽度/对齐指定

它也相当庞大,并且对 STLSoft 有严重的依赖,这可能会在有些项目中使用时过于严格。

Boost Spirit.Karma

这实际上不是一个格式化库,但我还是决定将其包括在此处以保持完整性。由于使用了 iostreams,因此它与将文本字面量与参数混合的问题相似。这个库的速度相当快,但在 Karma 自己的基准测试中,在整数格式化方面慢于使用格式字符串编译的 fmt::format_to,请参见每秒将一亿个整数转换为字符串

许可证

{fmt}采用MIT 许可证发行。

文档许可证

文档中的格式化字符串语法部分基于 Python string 模块文档。因此,该文档采用可在doc/python-license.txt中找到的 Python 软件基金会许可证分发。仅在分发 {fmt} 的文档时适用。

维护者

{fmt}库由 Victor Zverovich (vitaut) 和 Jonathan Müller (foonathan) 维护,许多其他人也提供了贡献。有关一些命名人士的信息,请参阅贡献者发行版。如果您在名单中未列出或提及不当,请告知我们,我们将更正。