{fmt}和ctre
{fmt}是C++20的新的格式化库,用来取代printf和iostream。ctre是捷克女程序员hana开发的编译时正则表达式库,用来取代std::regex。
fmt和ctre焦不离孟。在实现自定义的format时,需要解析format specifacation,这时用正则解析是非常方便的。
德国女程序员在meeting c++上做了 socializing with fmt 的精彩演讲。hana曾在cppcon c++now上多次详细解释ctre原理和性能等。
下文我们使用ctre 重新实现了socializing with fmt中format某种颜色的例子。可以看到我们parse时使用ctre挖取了format specifacation的四个子串,format函数利用parse得到的信息做定制化format。
/* extended specifacation language
extendedspec ::= [optinalExtension][formatspec ]
optionalExtension ::= "{" alternate [delimiter] "}"
delimiter ::= a single character other than '{', '}' or '\0'
alternate ::= ":" | "#"
*/
//
#include <fmt/format.h>
#include <fmt/color.h>
#include <iostream>
#include <vector>
#include <ctre.hpp>
#include <optional>
#include <iostream>
#include <tuple>
static constexpr auto pattern = ctll::fixed_string{ "^(\\{([:#])([^\\{\\}\\0])?\\})?[^\\}]*(.)\\}.*$" };
namespace fmt
{
template<class Char>
struct formatter<rgb, Char> :formatter<uint8_t, Char>
{
using base = formatter<uint8_t, Char>;
template<typename FormatContext>
auto format(const rgb& color, FormatContext& ctx)
{
auto oit = ctx.out();
if (isHex_)
{
if (alternate_ == '#')
{
oit = '#';
}
}
else {
if (alternate_ == ':')
{
oit = '{';
}
}
oit = base::format(color.r, ctx);
if (delimeter_)
{
oit = delimeter_;
}
oit = base::format(color.g, ctx);
if (delimeter_)
{
oit = delimeter_;
}
oit = base::format(color.b, ctx);
if ((!isHex_) && (alternate_ == ':'))
{
oit = '}';
}
return oit;
}
template<typename ParseContext>
auto parse(ParseContext& ctx)
{
if (auto op = extract_spec(std::string_view(ctx.begin(), ctx.end() - ctx.begin())))
{
auto [length, alternate, delimeter, lastChar] = *op;
ctx.advance_to(ctx.begin() + length);
isHex_ = (lastChar == "X" || lastChar == "x");
alternate_ = alternate[0];
delimeter_ = delimeter[0];
}
auto r = base::parse(ctx);
return r;
}
private:
char alternate_ = 0;
char delimeter_ = 0;
bool isHex_ = false;
std::optional<std::tuple<int, std::string_view, std::string_view, std::string_view>> extract_spec(std::string_view s) noexcept {
if (auto m = ctre::match<pattern>(s)) {
return std::make_tuple(
m.get<1>().to_view().size(), //optionalExtension length
m.get<2>().to_view(),//alternate
m.get<3>().to_view(),//delimeter
m.get<4>().to_view());//char before next '}', to know spec is Hex('x'|'X') or not.
}
else {
return std::nullopt;
}
}
};
}
int main()
{
{
fmt::rgb bg{ 90,90,10 };
fmt::rgb fg{ 127,0,0 };
fmt::print(fmt::bg(bg) | fmt::fg(fg), "{:{#-}02X},{:{:,}02}", bg, fg);
fmt::print("\n");
}
}
Posted 2020-08-12