C++ {fmt} 库,具有嵌套替换字段的用户定义类型?
C++ {fmt} library, user-defined types with nested replacement fields?
我正在尝试将 {fmt} 添加到我的项目中,一切进展顺利,除了我在尝试为我的简单 Vec2 添加用户定义类型时遇到了一点障碍 class。
struct Vec2 { float x; float y; };
我想要的是能够使用与基本内置 float 类型相同的格式 flags/arguments,但将其复制到 vec2 的 x 和 y 成员,并用括号括起它。
例如,只有一个浮点数:
fmt::format("Hello '{0:<8.4f}' World!", 1.234567);
// results in "Hello '1.2346 ' World!"
用我的 vec2 class:
Vec2 v {1.2345678, 2.3456789};
fmt::format("Hello '{0:<8}' World!", v);
// results in "Hello (1.2346 , 2.3457 )' World!"
但是当我们尝试使用嵌套替换字段时,我复制替换字段内容的幼稚方法不起作用。
例如,使用浮点数:
fmt::format("Hello '{0:<{1}.4f}' World!", 1.234567, 8);
// results in "Hello '1.2346 ' World!"
但是尝试使用我的 Vec2 类型...
Vec2 v {1.2345678, 2.3456789};
fmt::format("Hello '{0:<{1}.4f}' World!", v, 8);
// throws format_error, what(): "argument not found"
当然,发生这种情况是因为我所做的只是复制“:”之后和“}”之前的替换字段,并尊重 {} 平衡,所以如果我使用了嵌套替换字段, 里面会有一个 {} 引用原始列表中的一些参数,这对这个没有好处。
我对用户定义类型的专长:
struct Vec2
{
float x;
float y;
};
template<>
struct fmt::formatter<Vec2>
{
auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin())
{
int curlyBalance = 1;
auto it = ctx.begin(), end = ctx.end();
while (it != end)
{
if (*it == '}')
{
--curlyBalance;
}
else if (*it == '{')
{
++curlyBalance;
}
if (curlyBalance <= 0)
break;
else
++it;
}
const char* beginPtr = &(*ctx.begin());
const char* endPtr = &(*it);
size_t len = endPtr - beginPtr;
if (len == 0)
{
formatStr = "{}";
}
else
{
formatStr = "{0:";
formatStr += std::string(beginPtr, len + 1);
}
return it;
}
template <typename FormatContext>
auto format(const Vec2& vec, FormatContext& context)
{
fmt::format_to(context.out(), "(");
fmt::format_to(context.out(), formatStr, vec.x);
fmt::format_to(context.out(), ", ");
fmt::format_to(context.out(), formatStr, vec.y);
return fmt::format_to(context.out(), ")");
}
std::string formatStr;
};
int main()
{
std::cout << "Hello world!" << std::endl;
Vec2 v {1.234567, 2.345678};
// Simple, static width.
//std::string fmtResult = fmt::format("Hello '{0:<8.4f}' World!\n", v, 5);
// Dynamic width, oh god, oh dear god no!
std::string fmtResult = fmt::format("Hello '{0:<{1}}' World!\n", v, 5);
std::cout << fmtResult;
}
似乎需要做的是我的解析函数需要访问其他参数,以便它可以用正确的值填充嵌套替换字段...但我对这个库还是陌生的,非常感谢您对此提供的帮助!
您可以为此重复使用 formatter<float>
(https://godbolt.org/z/vb9c5ffd5):
template<> struct fmt::formatter<Vec2> : formatter<float> {
template <typename FormatContext>
auto format(const Vec2& vec, FormatContext& ctx) {
auto out = ctx.out();
*out = '(';
ctx.advance_to(out);
out = formatter<float>::format(vec.x, ctx);
out = fmt::format_to(out, ", ");
ctx.advance_to(out);
out = formatter<float>::format(vec.y, ctx);
*out = ')';
return out;
}
};
我正在尝试将 {fmt} 添加到我的项目中,一切进展顺利,除了我在尝试为我的简单 Vec2 添加用户定义类型时遇到了一点障碍 class。
struct Vec2 { float x; float y; };
我想要的是能够使用与基本内置 float 类型相同的格式 flags/arguments,但将其复制到 vec2 的 x 和 y 成员,并用括号括起它。
例如,只有一个浮点数:
fmt::format("Hello '{0:<8.4f}' World!", 1.234567);
// results in "Hello '1.2346 ' World!"
用我的 vec2 class:
Vec2 v {1.2345678, 2.3456789};
fmt::format("Hello '{0:<8}' World!", v);
// results in "Hello (1.2346 , 2.3457 )' World!"
但是当我们尝试使用嵌套替换字段时,我复制替换字段内容的幼稚方法不起作用。 例如,使用浮点数:
fmt::format("Hello '{0:<{1}.4f}' World!", 1.234567, 8);
// results in "Hello '1.2346 ' World!"
但是尝试使用我的 Vec2 类型...
Vec2 v {1.2345678, 2.3456789};
fmt::format("Hello '{0:<{1}.4f}' World!", v, 8);
// throws format_error, what(): "argument not found"
当然,发生这种情况是因为我所做的只是复制“:”之后和“}”之前的替换字段,并尊重 {} 平衡,所以如果我使用了嵌套替换字段, 里面会有一个 {} 引用原始列表中的一些参数,这对这个没有好处。
我对用户定义类型的专长:
struct Vec2
{
float x;
float y;
};
template<>
struct fmt::formatter<Vec2>
{
auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin())
{
int curlyBalance = 1;
auto it = ctx.begin(), end = ctx.end();
while (it != end)
{
if (*it == '}')
{
--curlyBalance;
}
else if (*it == '{')
{
++curlyBalance;
}
if (curlyBalance <= 0)
break;
else
++it;
}
const char* beginPtr = &(*ctx.begin());
const char* endPtr = &(*it);
size_t len = endPtr - beginPtr;
if (len == 0)
{
formatStr = "{}";
}
else
{
formatStr = "{0:";
formatStr += std::string(beginPtr, len + 1);
}
return it;
}
template <typename FormatContext>
auto format(const Vec2& vec, FormatContext& context)
{
fmt::format_to(context.out(), "(");
fmt::format_to(context.out(), formatStr, vec.x);
fmt::format_to(context.out(), ", ");
fmt::format_to(context.out(), formatStr, vec.y);
return fmt::format_to(context.out(), ")");
}
std::string formatStr;
};
int main()
{
std::cout << "Hello world!" << std::endl;
Vec2 v {1.234567, 2.345678};
// Simple, static width.
//std::string fmtResult = fmt::format("Hello '{0:<8.4f}' World!\n", v, 5);
// Dynamic width, oh god, oh dear god no!
std::string fmtResult = fmt::format("Hello '{0:<{1}}' World!\n", v, 5);
std::cout << fmtResult;
}
似乎需要做的是我的解析函数需要访问其他参数,以便它可以用正确的值填充嵌套替换字段...但我对这个库还是陌生的,非常感谢您对此提供的帮助!
您可以为此重复使用 formatter<float>
(https://godbolt.org/z/vb9c5ffd5):
template<> struct fmt::formatter<Vec2> : formatter<float> {
template <typename FormatContext>
auto format(const Vec2& vec, FormatContext& ctx) {
auto out = ctx.out();
*out = '(';
ctx.advance_to(out);
out = formatter<float>::format(vec.x, ctx);
out = fmt::format_to(out, ", ");
ctx.advance_to(out);
out = formatter<float>::format(vec.y, ctx);
*out = ')';
return out;
}
};