在C++中命名参数字符串格式

16 c++ string-formatting

我想知道是否有像Boost Format这样的库,但它支持命名参数而不是位置参数.这是一个常见的习惯用法,例如Python,你有一个上下文来格式化字符串,可能会或可能不会使用所有可用的参数,例如

mouse_state = {}
mouse_state['button'] = 0
mouse_state['x'] = 50
mouse_state['y'] = 30

#...

"You clicked %(button)s at %(x)d,%(y)d." % mouse_state
"Targeting %(x)d, %(y)d." % mouse_state
Run Code Online (Sandbox Code Playgroud)

是否有任何库提供最后两行的功能?我希望它能提供类似以下的API:

PrintFMap(string format, map<string, string> args);
Run Code Online (Sandbox Code Playgroud)

在谷歌搜索中,我发现许多库提供了位置参数的变化,但没有一个支持命名参数.理想情况下,库具有很少的依赖性,因此我可以轻松地将其放入我的代码中.C++不会像收集命名参数那样惯用,但可能有人在那里比我想得更多.

性能很重要,特别是我希望保持内存分配(在C++中总是很棘手),因为这可能在没有虚拟内存的设备上运行.但是,即使从一个缓慢的开始,也可能比从头开始自己编写更快.

vit*_*aut 8

FMT库支持命名参数:

print("You clicked {button} at {x},{y}.",
      arg("button", "b1"), arg("x", 50), arg("y", 30));
Run Code Online (Sandbox Code Playgroud)

或者,如果您有具有相同名称的局部变量:

print("You clicked {button} at {x},{y}.",
      "button"_a="b1", "x"_a=50, "y"_a=30);
Run Code Online (Sandbox Code Playgroud)

作为一个语法糖,你甚至可以(ab)使用用户定义的文字来传递参数:

print("You clicked {button} at {x},{y}.",
      arg("button", "b1"), arg("x", 50), arg("y", 30));
Run Code Online (Sandbox Code Playgroud)

为简洁起见fmt,在上面的例子中省略了命名空间.

免责声明:我是这个图书馆的作者.


650*_*502 6

我一直是评论家C++ I/O(尤其是格式化),因为在我看来是一个一步落后于对于C.格式需要是动态的,并且非常有意义,例如从外部资源加载它们作为一个文件或参数.

然而,我从未尝试过实际实施替代方案,而你的问题让我试图在周末时间内投入这个想法.

当然问题比我想象的要复杂得多(例如整数格式化例程只有200多行),但我认为这种方法(动态格式字符串)更有用.

您可以从此链接下载我的实验(它只是一个.h文件)和来自此链接的测试程序(测试可能不是正确的术语,我只是用它来查看我是否能够编译).

以下是一个例子

#include "format.h"
#include <iostream>

using format::FormatString;
using format::FormatDict;

int main()
{
    std::cout << FormatString("The answer is %{x}") % FormatDict()("x", 42);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它与boost.format方法不同,因为它使用命名参数,因为格式字符串和格式字典是分开构建的(例如传递).另外我认为格式化选项应该是字符串的一部分(比如printf)而不是代码.

FormatDict 使用技巧来保持语法合理:

FormatDict fd;
fd("x", 12)
  ("y", 3.141592654)
  ("z", "A string");
Run Code Online (Sandbox Code Playgroud)

FormatString而只是从一个解析const std::string&(我决定预先格式化字符串,但一个较慢但可能接受的方法只是传递字符串并每次重新分析它).

通过专门化转换函数模板,可以为用户定义的类型扩展格式; 例如

struct P2d
{
    int x, y;
    P2d(int x, int y)
        : x(x), y(y)
    {
    }
};

namespace format {
    template<>
    std::string toString<P2d>(const P2d& p, const std::string& parms)
    {
        return FormatString("P2d(%{x}; %{y})") % FormatDict()
            ("x", p.x)
            ("y", p.y);
    }
}
Run Code Online (Sandbox Code Playgroud)

之后,P2d可以将实例简单地放在格式化字典中.

还可以通过将参数放在%和之间来将参数传递给格式化函数{.

目前我只实现了支持的整数格式化特化

  1. 固定尺寸,左/右/中心对齐
  2. 自定义填充炭
  3. 通用基(2-36),小写或大写
  4. 数字分隔符(包含自定义字符和计数)
  5. 溢出字符
  6. 标志显示

例如,我还为常见案例添加了一些快捷方式

"%08x{hexdata}"
Run Code Online (Sandbox Code Playgroud)

是一个十六进制数,8位用"0"填充.

"%026/2,8:{bindata}"
Run Code Online (Sandbox Code Playgroud)

是一个24位二进制数(根据需要"/2"),":"每8位数字分隔符(根据需要",8:").

请注意,代码只是一个想法,例如,现在我只是在允许存储格式字符串和字典可能是合理的时候阻止复制(对于字典而言,重要的是提供避免复制对象的能力,因为它需要被添加到a FormatDict,而IMO这是可能的,这也是引起关于生命期的非平凡问题的东西).

UPDATE

我对初始方法做了一些修改:

  1. 现在可以复制格式字符串
  2. 使用模板类而不是函数来完成自定义类型的格式化(这允许部分特化)
  3. 我为序列添加了一个格式化程序(两个迭代器).语法仍然很粗糙.

我已经为它创建了一个github项目,并获得了boost许可.


小智 3

答案似乎是,不,没有一个 C++ 库可以做到这一点,而且根据我收到的评论,C++ 程序员显然甚至没有看到需要一个库。我将不得不再次写自己的。

  • 这不再是真的=)。有一个 C++ 库可以执行此操作:https://github.com/cppformat/cppformat (2认同)