User Defined Literals (UDL) produces an object in an interesting way:
constexpr auto operator""_f(const char* fmt, size_t) {
return[=]<typename... T>(T&&... Args) { return std::vformat(fmt, std::make_format_args(std::forward<T>(Args)...)); };
}
auto s = "example {} see {}"_f("yep", 1.1); // s = "example yep 1.1"
The UDL _f
has the same effect of std::format("example {} see {}", "yep", 1.1)
. Pretty familiar (as libfmt), right?
Now, let’s break the definition of _f
down:
int x = 10;
double y = 3.14;
// 0. std::string format( std::format_string<Args...> fmt, Args&&... args );
// basic format string.
// it is an error if the format string is not a constant expression, i.e., known at compile-time,
// std::vformat can be used in this case.
std::string s = std::format("x is {} and y is {}", x, y);
// 1. std::string vformat( std::string_view fmt, std::format_args args );
// It's useful in scenarios where you need to pass around the arguments
// separately from the format string
auto args = std::make_format_args(x, y);
std::string s = std::vformat("x is {} and y is {}", args);
// 2. a variadic lambda template
return [=]<typename... T>(T&&... Args) {...}
// 3. user-defined literals
// this UDL returns variadic lambda template
// e.g., auto str = "haha"_f(x, y)
// -> auto f = "haha"_f; auto str = f(x,y);
constexpr auto operator""_f(const char* fmt, size_t) {
return [=]<typename... T>(T&&... Args) {...}
}
// 4. BUG! While the lambda itself is constexpr and the captured fmt
// is a pointer to a string literal (which is also a compile-time constant),
// the usage of fmt inside the lambda isn't considered a constant expression
// in the strictest sense when passed to std::format.
for constexpr auto operator""_f(const char* fmt, size_t) {
return [=]<typename... T>(T&&... Args) {
return std::format(fmt, std::forward<T>(Args)...);
};
}
//***************************//
//Combining all above, we get:
//***************************//
constexpr auto operator""_f(const char* fmt, size_t) {
return[=]<typename... T>(T&&... Args) { return std::vformat(fmt, std::make_format_args(std::forward<T>(Args)...)); };
}
//e.g., put the "yep" string into the {} of the example string.
auto s = "example {}"_f("yep");