BOOST_CHECK无法编译运算符<<对于自定义类型

时间:2013-07-10 13:57:27

标签: c++ unit-testing boost

我写了这个非常琐碎的课程,以便清楚我的问题是什么:

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(第一个,第二个)应该通过似乎很自然。但是,在尝试这样做时我遇到了50个错误,第一个听起来像:运算符没有数学<<在ostr<<这是升级代码中的某个地方...其他测试工作得很好,比较已知类型甚至是指针,但是类对象似乎发生了一些不同的事情。

4 个答案:

答案 0 :(得分:22)

我使用operator<<确定了三种解决问题的方法。

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

第二种方法是不做我刚刚提到的日志记录。您可以#definine BOOST_TEST_DONT_PRINT_LOG_VALUE执行此操作。要仅针对一项测试禁用日志记录,您可以使用此#define围绕相关测试,然后立即#undef

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

第三种方法是通过不将一个项目与另一个项目进行比较,而只是检查一个bool来回避对operator<<适用于您的类型的需求:

BOOST_CHECK (first == second);

选择您喜欢的方法。


我的偏好是第一次,但实施这一点令人惊讶地具有挑战性。如果您只是在全局范围内定义operator<<,那么它将无法工作。我认为这是因为名称解析问题。解决此问题的一个流行建议是将operator<<放在std命名空间中。这在某些编译器中至少在实践中有效,但我不喜欢它,因为标准禁止向std命名空间添加任何内容。

我发现一种更好的方法是为您的类型实现自定义print_log_value类模板特化。 print_log_value是Boost.Test内部的类模板useb,实际上为指定的类型调用了正确的operator<<。它委托operator<<进行繁重的工作。专业print_log_value为您的自定义类型由Boost [需要引证]正式支持,并由此完成。

假设您的类型名为Timestamp(位于我的代码中),请先为operator<<定义全局免费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);
}
};                                                          
}}

答案 1 :(得分: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

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

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]

答案 2 :(得分:3)

这是优秀answer from John Dibling的补充。问题似乎是在正确的命名空间中需要一个输出操作符。因此,如果您定义了全局输出operator<<,那么您可以通过在boost::test_tools::tt_detail命名空间中定义另一个转发到的错误来避免此错误(至少使用Visual Studio 2015,即vc14和boost 1.60)全球运营商。这个小调整可以避免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 documentation中清楚地看到这个问题。

答案 3 :(得分:2)

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

下面给出了文档中的一个例子。我们的想法是为您要打印的类型定义函数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);
}