2013-07-10 77 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); 
}