在 C++ {fmt} 中格式化普通结构的扩展计划?
Extension plans for formatting of trivial structs in C++ {fmt}?
在我当前的项目中,我需要一个非常简单的自定义“序列化”表示形式,表示几个普通的数据传输对象 (DTO) 结构。在第一次提出一个定制的解决方案之后,那是一个很大的 PITA,我有了使用 {fmt} 的想法。
所以最近几天我一直在尝试通过 fmt::formatter
模板专门化机制的扩展来格式化自定义类型。因此 this blog and your docs 非常有帮助。
经过一番折腾,我想出了一个非常通用的 poc 解决方案,它允许以多种自定义格式格式化结构,看起来有点像以下内容:
struct Inner {
double x;
std::string y;
int z;
};
struct Outer {
int a;
std::string b;
Inner inner;
};
template<>
struct reflection<Outer> {
/*definition of class name and field names has to be provided manually...*/
};
template<>
struct reflection<Inner> {
/*definition of class name and field names has to provided manually...*/
};
/*
...
couple dozend lines of meta programming and fmt::formatter specializations.
...
*/
auto outer = Outer{.a=1,.b="hello",.inner={.x=3.12,.y=" ",.z=2}};
std::string simple = fmt::format("{:s}", outer); // :s means format as simple
assert(simple == "a|hello|3.12| |2");
assert(fmt::format("{:s;}", outer) == "a;hello;3.12; ;2");
std::string extended = fmt::format("{:e}",outer); // :e means format as extended
assert(extended == "Outer{.a=1, .b=hello, .inner=Inner{.x=3.12, .y= , .z=2}}");
显然,没有标准的方法来反映字段和结构的名称,因此必须手动提供反射结构,或者例如通过宏魔术。但这是一个不同的话题,我不想在这里讨论。 - 如果幸运的话,我们在 c++23 \o/ 中得到了一些最小的编译时反射。让我们期待吧!
我把所有这些放在一起 this repo。
真题:
通过 {fmt} 提供的简单反射 API 格式化用户定义的类型是否是您认为未来可能扩展到 {fmt} 的东西?
我想象一个场景,预定义了几个简单的格式化模式,用户只需要为他的类型提供反射。
有了这个,我什至可以看到像 fmt::format("{:json,prety,tabwith=4}", outer)
这样的格式化表达式。
此外,也许我只是在重新发明轮子,所以如果有类似的东西,基于那里的 {fmt} - 告诉我! :)
无论如何,感谢您为社区提供了很棒的工具,并祝贺您将其纳入 c++20!
此致,马丁
Would formatting user defined types via a simple reflection API provided by {fmt} be something that you'd consider a possible future extension to {fmt}?
可能,但它必须是明确的选择加入,即此类类型不会以这种方式自动格式化,因为通常您需要更高级别的表示而不是字段集合。例如,您可能希望将点的格式设置为 (x, y)
而不是 Point{.x=x, .y=y}
.
在我当前的项目中,我需要一个非常简单的自定义“序列化”表示形式,表示几个普通的数据传输对象 (DTO) 结构。在第一次提出一个定制的解决方案之后,那是一个很大的 PITA,我有了使用 {fmt} 的想法。
所以最近几天我一直在尝试通过 fmt::formatter
模板专门化机制的扩展来格式化自定义类型。因此 this blog and your docs 非常有帮助。
经过一番折腾,我想出了一个非常通用的 poc 解决方案,它允许以多种自定义格式格式化结构,看起来有点像以下内容:
struct Inner {
double x;
std::string y;
int z;
};
struct Outer {
int a;
std::string b;
Inner inner;
};
template<>
struct reflection<Outer> {
/*definition of class name and field names has to be provided manually...*/
};
template<>
struct reflection<Inner> {
/*definition of class name and field names has to provided manually...*/
};
/*
...
couple dozend lines of meta programming and fmt::formatter specializations.
...
*/
auto outer = Outer{.a=1,.b="hello",.inner={.x=3.12,.y=" ",.z=2}};
std::string simple = fmt::format("{:s}", outer); // :s means format as simple
assert(simple == "a|hello|3.12| |2");
assert(fmt::format("{:s;}", outer) == "a;hello;3.12; ;2");
std::string extended = fmt::format("{:e}",outer); // :e means format as extended
assert(extended == "Outer{.a=1, .b=hello, .inner=Inner{.x=3.12, .y= , .z=2}}");
显然,没有标准的方法来反映字段和结构的名称,因此必须手动提供反射结构,或者例如通过宏魔术。但这是一个不同的话题,我不想在这里讨论。 - 如果幸运的话,我们在 c++23 \o/ 中得到了一些最小的编译时反射。让我们期待吧!
我把所有这些放在一起 this repo。
真题:
通过 {fmt} 提供的简单反射 API 格式化用户定义的类型是否是您认为未来可能扩展到 {fmt} 的东西? 我想象一个场景,预定义了几个简单的格式化模式,用户只需要为他的类型提供反射。
有了这个,我什至可以看到像 fmt::format("{:json,prety,tabwith=4}", outer)
这样的格式化表达式。
此外,也许我只是在重新发明轮子,所以如果有类似的东西,基于那里的 {fmt} - 告诉我! :)
无论如何,感谢您为社区提供了很棒的工具,并祝贺您将其纳入 c++20!
此致,马丁
Would formatting user defined types via a simple reflection API provided by {fmt} be something that you'd consider a possible future extension to {fmt}?
可能,但它必须是明确的选择加入,即此类类型不会以这种方式自动格式化,因为通常您需要更高级别的表示而不是字段集合。例如,您可能希望将点的格式设置为 (x, y)
而不是 Point{.x=x, .y=y}
.