2013-07-10 45 views
6

我写这真是小巫见大巫类,以便它很清楚我的问题是什么:BOOST_CHECK编译失败操作<<自定义类型

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

现在,如果我定义了一个第一(1)和第二(1 ),我认为BOOST_CHECK_EQUAL(第一,第二)应该可以通过,这似乎很自然。然而,当我试图这样做时,我得到了50个错误,第一个听起来像:没有数学运算符< <在ostr < < t这是在升压代码中的某处...其他测试工作得很好,比较已知类型或甚至指针,但是类对象看起来有些不同。

+2

什么是错误? – Dennis

+0

它不编译...而且我在Codeblocks中工作,所以我不能复制粘贴错误。他们都在文件test_tools.hpp中,我真的不明白他们是什么(我提到了第一篇文章)。我应该给更多的例子吗? – Ioana

+0

我使用gcc进行编译,我想粘贴这些错误,但11400个字符太长,无法发表评论。 – Ioana

回答

17

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

第一种方法是为您的类型提供operator<<。这是因为当boost_check_equal失败时,它还通过调用operator<<与对象来记录失败。看看中断后的详细附录,看看它是如何实现的。这看起来比看起来更难。

第二种方法是不做我刚刚提到的日志记录。您可以通过#definine ing 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 

第三种方式是回避一个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(这是在我的代码),首先定义Timestamp全球自由operator<<

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); 
} 
};               
}} 
+0

我明白了,谢谢,我现在要做一个简单的boost_check(),因为我真的需要非常紧急地测试一些事情,但我会在后面考虑其他方法。 – Ioana

+3

至少从boost_1.61.00开始,你必须在'boost :: test_tools :: tt_detail'内指定'print_log_value'模板。 – Ace7k3

+0

感谢您的更新,@ Ace7k3 –

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] 
+0

+1:现在就看到这个,我是粉丝。当试图转储诸如UDP数据包之类的东西时,这样的东西非常方便。 –

2

这是对优秀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论述清楚。

1

有一种干净的方式启动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); 
}