c++ - 有没有办法教 gtest 使用 libfmt 的格式化程序打印用户定义的类型?

标签 c++ c++17 googletest

我想知道是否有一种方法可以让 gtest 理解用户定义类型 libfmt 的格式化程序,以便打印可读的错误输出? 我知道如何通过添加流插入运算符 operator<< 来教 gtest 理解用户定义的类型对于这种用户定义的类型,例如

std::ostream& operator<<(std::ostream& stream, CpuTimes const& anything);
std::ostream& operator<<(std::ostream& stream, CpuStats const& anything);

这是有据可查的 here .

但我大量使用 libfmt,它需要实现格式化函数来生成用户定义类型的可读且可打印的输出。这个所谓的fmt::formatter实际上是模板特化,例如

namespace fmt {

template <> struct formatter<CpuTimes> : basics::fmt::ParseContextEmpty {
    format_context::iterator format(CpuTimes const& times, format_context& ctx);
};
template <> struct formatter<CpuStats> : basics::fmt::ParseContextEmpty {
    format_context::iterator format(CpuStats const& stats, format_context& ctx);
};
template <> struct formatter<CpuLimits> : basics::fmt::ParseContextEmpty {
    format_context::iterator format(CpuLimits const& limits, format_context& ctx);
};
} // namespace fmt

为了让 gtest 理解这种格式,您必须为 operator<< 编写相同的实现。一遍又一遍地检查您希望 gtest 正确打印的每种类型,例如

std::ostream& operator<<(std::ostream& stream, CpuTimes const& anything) {
    return stream << fmt::format("{}", anything);
}
std::ostream& operator<<(std::ostream& stream, CpuStats const& anything) {
    return stream << fmt::format("{}", anything);
}
std::ostream& operator<<(std::ostream& stream, CpuLimits const& anything) {
    return stream << fmt::format("{}", anything);
}

有没有办法让我不用编写这些样板代码?

最佳答案

避免 PrintTo 的歧义引起的问题或operator<<需要命名空间。

这里是some demo当使用命名空间时:

#include <fmt/format.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

namespace me {
struct Foo {
    int x = 0;
    double y = 0;
};

bool operator==(const Foo& a, const Foo& b)
{
    return a.x == b.x && a.y == b.y;
}
}

template <>
struct fmt::formatter<me::Foo> {
    char presentation = 'f';

    constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin())
    {
        auto it = ctx.begin(), end = ctx.end();
        if (it != end && (*it == 'f' || *it == 'e'))
            presentation = *it++;
        if (it != end && *it != '}')
            throw format_error("invalid format");
        return it;
    }

    template <typename FormatContext>
    auto format(const me::Foo& p, FormatContext& ctx) -> decltype(ctx.out())
    {
        return presentation == 'f'
            ? format_to(ctx.out(), "({}, {:.1f})", p.x, p.y)
            : format_to(ctx.out(), "({}, {:.1e})", p.x, p.y);
    }
};

#if VERSION == 1

namespace me {
template <typename T>
void PrintTo(const T& value, ::std::ostream* os)
{
    *os << fmt::format(FMT_STRING("{}"), value);
}
}

#elif VERSION == 2

namespace me {
template <typename T>
std::ostream& operator<<(std::ostream& out, const T& value)
{
    ::std::operator<<(out, fmt::format(FMT_STRING("{}"), value));
    return out;
}
}
#endif

class MagicTest : public testing::Test { };

TEST_F(MagicTest, CheckFmtFormater)
{
    EXPECT_EQ(fmt::format("{}", me::Foo {}), "(0, 0.0)");
}

TEST_F(MagicTest, FailOnPurpuse)
{
    EXPECT_EQ(me::Foo {}, (me::Foo { 1, 0 }));
}

删除me命名空间导致所有实现 have ambiguity problems .

请注意,命名空间会导致 argument-dependent lookup已使用。

关于c++ - 有没有办法教 gtest 使用 libfmt 的格式化程序打印用户定义的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72954344/

相关文章:

c++ - 根据编译器优化和代码性能,`if constexpr` 与 `if`

c++ - std::map 的复制列表初始化,其中 std::variant 作为mapped_type

c++ - 在 Halide 管道中访问函数

c++ - 如何使用 std::mt19937 避免相同的随机结果?

c++ - 模板和继承打破了严格的别名规则

c++ - 如何从googletest中的异常中获取回溯信息?

c++ - 未定义对 Gtest 的 pthread 的引用

c++ - 如何以编程方式调用具有不同数据类型的 C++ 模板函数?

c++ - 使用临时存储区 : is it allowed? 复制可简单复制的类型

jenkins - 在 Jenkins 中使用 gtest