在 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}.