我想知道是否有像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
是否有任何库提供最后两行的功能?我希望它提供类似的API:
PrintFMap(string format, map<string, string> args);
在谷歌搜索中,我发现许多库提供了位置参数的变化,但没有一个支持命名参数。理想情况下,库具有很少的依赖性,因此我可以轻松地将其放入我的代码中。 C ++不会像收集命名参数那样惯用,但可能有人在那里比我想得更多。
性能很重要,特别是我希望保持内存分配(在C ++中总是很棘手),因为这可能在没有虚拟内存的设备上运行。但是,即使从一个缓慢的开始,也可能比从头开始自己写的更快。
答案 0 :(得分:6)
我一直都是C ++ I / O(特别是格式化)的评论家,因为在我看来,C语言的步骤是向后。格式需要是动态的,并且例如完全有意义从外部资源加载它们作为文件或参数。
我之前从未尝试过实际实施替代方案,而你的问题让我试图在周末时间内投入这个想法。
当然问题比我想象的要复杂得多(例如整数格式化例程只有200多行),但我认为这种方法(动态格式字符串)更有用。
你可以从this link(它只是一个.h文件)和this link的测试程序下载我的实验(测试可能不是正确的术语,我用它来看我是不是能够编译)。
以下是一个例子
#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;
}
它与boost.format方法不同,因为使用命名参数和因为
格式字符串和格式字典应单独构建(并用于
传递的例子)。另外我认为格式化选项应该是其中的一部分
字符串(如printf
)而不在代码中。
FormatDict
使用一种技巧来保持语法的合理性:
FormatDict fd;
fd("x", 12)
("y", 3.141592654)
("z", "A string");
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);
}
}
之后,P2d
实例可以简单地放在格式化词典中。
也可以通过将参数放在%
和{
之间来将参数传递给格式化函数。
目前我只实现了支持
的整数格式化特化我还为常见案例添加了一些快捷方式,例如
"%08x{hexdata}"
是一个十六进制数字,8位用“0”填充。
"%026/2,8:{bindata}"
是一个24位二进制数(根据"/2"
的要求),每8位数字分隔符":"
(根据",8:"
的要求)。
请注意,代码只是一个想法,例如,现在我只是阻止了副本,因为允许存储格式字符串和字典可能是合理的(对于字典来说,重要的是提供避免复制对象的能力,因为它需要被添加到FormatDict
,而IMO这是可能的,它也会引起关于生命周期的非平凡问题。)
我对初始方法做了一些修改:
我创建了github project for it,并获得了许可。
答案 1 :(得分:6)
fmt library支持命名参数:
print("You clicked {button} at {x},{y}.",
arg("button", "b1"), arg("x", 50), arg("y", 30));
或者,如果您有具有相同名称的局部变量:
const char *button = b1;
int x = 50, y = 30;
print("You clicked {button} at {x},{y}.", FMT_CAPTURE(button, x, y));
作为一种语法糖,你甚至可以(ab)使用用户定义的文字来传递参数:
print("You clicked {button} at {x},{y}.",
"button"_a="b1", "x"_a=50, "y"_a=30);
为简洁起见,上面的示例中省略了名称空间fmt
。
免责声明:我是这个图书馆的作者。
答案 2 :(得分:2)
答案似乎是,不,没有一个C ++库可以做到这一点,而C ++程序员显然甚至根据我收到的评论看不到需要它。我将不得不再写一次。
答案 3 :(得分:1)
那么我也会添加自己的答案,不是我知道(或编码过)这样的库,而是回答“保持内存分配”这一点。
一如既往,我可以设想某种速度/记忆权衡。
一方面,你可以解析“及时”:
class Formater:
def __init__(self, format): self._string = format
def compute(self):
for k,v in context:
while self.__contains(k):
left, variable, right = self.__extract(k)
self._string = left + self.__replace(variable, v) + right
这样你就不会保留一个“解析”结构了,希望大多数时候你只需要插入新数据(与Python不同,C ++字符串不是不可变的)。
然而,它远没有效率......
另一方面,您可以构建一个表示已解析格式的完全构造的树。您将拥有以下几个类:Constant
,String
,Integer
,Real
等等......可能还有一些子类/装饰器以及格式化本身。
Constant
,Variable
Loki::AssocVector
)。你有:你只完成了2个动态分配的数组(基本上)。如果您想允许多次重复相同的密钥,只需使用std::vector<size_t>
作为索引的值:良好的实现不应为小型向量动态分配任何内存(VC ++ 2010不会低于16字节的数据)。
在评估上下文本身时,查找实例。然后,您可以“及时”解析格式化程序,检查它的当前类型以替换它,并处理格式。
利弊: - 及时:你一次又一次地扫描字符串 - One Parse:需要很多专用类,可能需要很多分配,但格式在输入时有效。像Boost一样,它可以重复使用。 - 混合:效率更高,特别是如果你不替换某些值(允许某种“null”值),但是延迟解析格式会延迟报告错误。
就个人而言,我会选择One Parse
计划,尝试尽可能地使用boost::variant
和策略模式来保持分配。
答案 4 :(得分:0)
鉴于Python它的自身是用C语言编写的,并且格式化是一种常用的功能,你可能能够(忽略拷贝写入问题)从python解释器中删除相关代码并将其移植到使用STL映射而不是Pythons native dicts。
答案 5 :(得分:0)
我为这个傀儡写了一个图书馆,请在GitHub上查看。
贡献很好。