11

我写了这个非常简单的类,以便清楚我的问题是什么:

class A
{
public:
    int x;
    A(int y) {x=y;}
    bool operator==(const A &other) const {return x==other.x;}
};

现在,如果我定义 A first(1) 和 A second(1),那么 BOOST_CHECK_EQUAL(first, second) 应该通过对我来说似乎很自然。但是,我在尝试执行此操作时遇到 50 个错误,第一个听起来像: no math for operator << in ostr << t 这是 boost 代码中的某处......其他测试工作得很好,比较已知类型甚至指针,但是类对象似乎发生了一些不同的事情。

4

4 回答 4

27

我已经确定了三种解决问题的方法operator<<

第一种方法是operator<<为您的类型提供一个。这是必需的,因为当boost_check_equal失败时,它还会通过调用operator<<对象来记录失败。请参阅休息后的详细附录,以了解这是如何实际完成的。这比看起来更难。

第二种方法是不做我刚才提到的日志记录。你可以通过#definineing来做到这一点BOOST_TEST_DONT_PRINT_LOG_VALUE。要仅对一项测试禁用日志记录,您可以用 this 包围有问题的测试#define,然后立即使用#undef它:

#define BOOST_TEST_DONT_PRINT_LOG_VALUE
BOOST_CHECK_EQUAL (first, second);
#undef BOOST_TEST_DONT_PRINT_LOG_VALUE

第三种方法是通过不将一个项目与另一个项目进行比较,而只是检查一个布尔值来回避operator<<与您的类型一起工作的需要:

BOOST_CHECK (first == second);

选择您喜欢的方法。


我的偏好是第一个,但实施起来非常具有挑战性。如果您只是operator<<在全局范围内定义一个,它将不起作用。我认为这是因为名称解析存在问题。解决此问题的一个流行建议是将 放在operator<<命名std空间中。这至少在某些编译器上是有效的,但我不喜欢它,因为标准禁止向std命名空间添加任何内容。

我发现一个更好的方法是print_log_value为您的类型实现自定义类模板特化。 print_log_value是 Boost.Test 内部使用的类模板,用于实际调用operator<<指定类型的正确值。它委托给一个operator<<做繁重的工作。print_log_valueBoost [需要引用] 正式支持 专门针对您的自定义类型,并因此完成。

假设您的类型被调用Timestamp(它在我的代码中),首先定义一个全局 free operator<<for Timestamp

static inline std::ostream& operator<< (std::ostream& os, const Mdi::Timestamp& ts)
{
    os << "Timestamp";
    return os;
}   

...然后为其提供print_log_value专业化,委托给operator<<您刚刚定义的:

namespace boost { namespace test_tools {
template<>           
struct print_log_value<Mdi::Timestamp > {
void operator()( std::ostream& os,
    Mdi::Timestamp const& ts)
{
    ::operator<<(os,ts);
}
};                                                          
}}
于 2013-07-10T14:22:39.687 回答
4

根据John Dibling的回答,我正在寻找一种以十六进制而不是十进制转储整数值的方法,我想出了这种方法:

// test_macros.h in my project
namespace myproject
{
namespace test
{
namespace macros
{
    extern bool hex;

    // context manager
    struct use_hex
    {
        use_hex()  { hex = true; }
        ~use_hex() { hex = false; }
    };

 }; // namespace
 }; // namespace
 }; // namespace

namespace boost
{
namespace test_tools
{

    // boost 1.56+ uses these methods

    template<>
    inline                                               
    void                                                 
    print_log_value<uint64>::                       
    operator()(std::ostream & out, const uint64 & t) 
    {                                                    
        if(myproject::test::macros::hex)                    
            out << ::boost::format("0x%016X") % t;           
        else 
            out << t;                                        
    }                                                    

namespace tt_detail
{

    // Boost < 1.56 uses these methods

    template <>
    inline
    std::ostream &
    operator<<(std::ostream & ostr, print_helper_t<uint64> const & ph )
    {
        if(myproject::test::macros::hex)
            return ostr << ::boost::format("0x%016X") % ph.m_t;

        return ostr << ph.m_t;
    }

}; // namespace
}; // namespace
}; // namespace

现在在我的单元测试用例中,我可以通过设置全局静态布尔值来打开/关闭十六进制,例如:

for(uint64 i = 1; i <= 256/64; ++i)
{
    if(i % 2 == 0) test::macros::hex = true;
    else           test::macros::hex = false;
    BOOST_CHECK_EQUAL(i+1, dst.pop());
}

我得到了我正在寻找的行为:

test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [2 != 257]
test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [0x0000000000000003 != 0x0000000000000102]
test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [4 != 259]
test_foobar.cc(106): error in "test_foobar_51": check i+1 == dst.pop() failed [0x0000000000000005 != 0x0000000000000104]

或者,我可以使用上下文管理器:

{
    test::macros::use_hex context;

    for(uint64 i = 1; i <= 4; ++i)
    {
        BOOST_CHECK_EQUAL(i + 0x200, i + 0x100);
    }
}

for(uint64 i = 1; i <= 4; ++i)
{
    BOOST_CHECK_EQUAL(i + 0x200, i + 0x100);
}

并且十六进制输出将仅在该块中使用:

test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000201 != 0x0000000000000101]
test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000202 != 0x0000000000000102]
test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000203 != 0x0000000000000103]
test_foobar.cc(94): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [0x0000000000000204 != 0x0000000000000104]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [513 != 257]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [514 != 258]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [515 != 259]
test_foobar.cc(100): error in "test_foobar_28": check i + 0x200 == i + 0x100 failed [516 != 260]
于 2014-02-21T22:03:20.097 回答
4

这是对John Dibling 出色回答的补充。问题似乎是在正确的命名空间中需要有一个输出运算符。因此,如果您定义了全局输出,那么您可以通过在命名空间中定义另一个转发给全局运算符的operator<<错误来避免此错误(至少在 Visual Studio 2015、aka vc14 和 boost 1.60 中) 。boost::test_tools::tt_detail这一微小的调整使人们能够避免print_log_value类的奇怪和更冗长的专业化。这是我所做的:

namespace boost {
namespace test_tools {
namespace tt_detail {
std::ostream& operator<<(std::ostream& os, Mdi::Timestamp const& ts)
{
    return ::operator<<(os, ts);
}
}  // namespace tt_detail
}  // namespace test_tools
}  // namespace boost

虽然这个问题和答案发布已经三年了,但我还没有在Boost.Test 文档中看到清楚地讨论过这个问题。

于 2016-11-22T16:36:43.127 回答
3

启动 Boost 1.64 有一种干净的方法可以通过自定义点记录用户定义的类型。可以在此处找到此功能的完整文档。

下面给出了文档中的一个示例。这个想法是为boost_test_print_type您要打印的类型定义函数,并将此函数带入测试用例(通过 ADL 找到):

#define BOOST_TEST_MODULE logger-customization-point
#include <boost/test/included/unit_test.hpp>

namespace user_defined_namespace {
  struct user_defined_type {
      int value;

      user_defined_type(int value_) : value(value_)
      {}

      bool operator==(int right) const {
          return right == value;
      }
  };
}

namespace user_defined_namespace {
  std::ostream& boost_test_print_type(std::ostream& ostr, user_defined_type const& right) {
      ostr << "** value of user_defined_type is " << right.value << " **";
      return ostr;
  }
}

BOOST_AUTO_TEST_CASE(test1)
{
    user_defined_namespace::user_defined_type t(10);
    BOOST_TEST(t == 11);

    using namespace user_defined_namespace;
    user_defined_type t2(11);
    BOOST_TEST(t2 == 11);
}
于 2017-06-28T19:18:00.343 回答