C++20格式化库是使用类printf函数或I/O流库的替代方案,它实际上补充了这些功能。虽然该标准为基本类型(如整型和浮点型、bool型、字符类型、字符串和时间类型)提供了默认格式,但用户也可以为用户自定义类型创建自定义的规范。在本节中,我们将学习如何实现这一点。
你应该先阅读2.13节,以便熟悉格式化库。
在接下来所展示的示例中,我们将使用以下类:
在下一小节中,我们将介绍使用std::format()对用户自定义类型进行文本格式化的必要步骤。
要使用新的格式化库格式化用户自定义类型,必须执行以下操作:
❍ 在std命名空间中定义std::formatter<T,CharT>类的特化。
❍ 实现parse()方法来解析与当前参数对应的格式化字符串的部分。如果该类继承自另一个格式化器,则可以省略此方法。
❍ 实现format()方法来格式化参数并通过format_context将之写入输出文本。
对于这里列出的employee类,可以实现一个将employee格式化为[42] John Doe(即[id] firstName lastName)的格式化器,它的实现如下所示:
格式化库使用std::formatter<T,CharT>类模板定义给定类型的格式化规则。内置类型、字符串类型和时间类型都有标准库提供的格式化器,它们是std::formatter<T,CharT>类模板的特化。
这个类有两个方法:
❍ parse(),它接受一个类型为std::basic_format_parse_context<CharT>的参数,并解析由解析上下文提供的类型T的格式规范,解析的结果应该存储在类的成员字段中。如果解析成功,该函数应该返回std::basic_format_parse_context::iterator类型的值,它表示格式规范结束。如果解析失败,该函数应该抛出std::format_error类型的异常,以提供有关错误的详细信息。
❍ format(),它接受两个参数,第一个是要格式化的类型T的对象,第二个是类型为std::basic_format_context<OutputIt,CharT>的格式化上下文对象。该函数应该根据所需的说明符(可能是隐式的或解析格式规范的结果)将输出写入ctx.out()。该函数必须返回std::basic_format_context<OutputIt,CharT>::it erator类型的值,表示输出结束。
在这里展示的实现中,parse()函数除了返回一个表示格式规范开头的迭代器外,不做任何其他事情。格式化始终在方括号之间打印员工工号,然后打印姓和名,比如[24] John Doe。尝试使用格式说明符将导致运行时异常:
如果希望格式说明符支持用户自定义类型,那么必须正确地实现parse()方法。为了说明这是如何实现的,我们将使employee类支持L说明符。当使用该说明符时,将使用带方括号的工号后跟姓和名(同逗号分隔)对employee进行格式化,例如[42] Doe, John:
定义了这个之后,前面的示例代码就可以正常工作了。但是,使用其他格式说明符(例如A)仍然会抛出异常:
如果不需要解析用于适配各种选项的格式说明符,则可以完全忽略parse()方法。然而,为了做到这一点,std::formatter特化仍然必须派生自另一个std::formatter类,其实现如下所示:
这样,employee类的特化与2.14.2节例子中实现的功能一致。
❍ 阅读2.13节,以了解新的C++20文本格式化库。