2013-07-10 11 views
6

Napisałem to naprawdę trywialne klasę tak, że jest jasne, jaki jest mój problem:BOOST_CHECK nie skompilować operator << dla typów niestandardowych

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

Teraz, jeśli mogę zdefiniować pierwszą (1) i drugą (1), wydaje mi się naturalne, że BOOST_CHECK_EQUAL (pierwszy, drugi) powinien przejść. Mam jednak 50 błędów, gdy próbuję to zrobić, pierwszy brzmi: brak matematyki dla operatora < < w ostr < < t który jest gdzieś w kodzie pobudzenia ... Inne testy działają dobrze, porównując znane typy lub nawet wskaźników, ale jest coś innego, co zdarza się w przypadku obiektów klasowych.

+2

Jakie były błędy? – Dennis

+0

Nie kompiluje ... A ja pracuję w Codeblocks, więc nie mogę skopiować i wkleić błędów. I wszystkie są w pliku test_tools.hpp i naprawdę nie rozumiem o czym są (wspomniałem o pierwszej w poście). Czy powinienem podać więcej przykładów? – Ioana

+0

Skompilowałem z gcc i chciałbym wkleić błędy, ale jest za 11400 znaków zbyt długo na komentarz. – Ioana

Odpowiedz

17

Są trzy sposoby, które zidentyfikowałem, aby rozwiązać problem z operator<<.

Pierwszy sposób to dostarczyć operator<< dla swojego typu. Jest to potrzebne, ponieważ w przypadku niepowodzenia boost_check_equal rejestruje on również niepowodzenie, wywołując obiekty operator<<. Zobacz pełny dodatek po przerwie, aby zobaczyć, jak to się naprawdę robi. To jest trudniejsze, niż mogłoby się wydawać.

Drugi sposób to nie rejestrowanie, o którym wspomniałem. Możesz to zrobić przez #definine ing BOOST_TEST_DONT_PRINT_LOG_VALUE. Aby wyłączyć rejestrowanie tylko jednego testu, można otoczyć dany test z tego #define, a następnie natychmiast #undef go:

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

Trzecim sposobem jest ominąć potrzebę operator<< który współpracuje z typu, nie porównując jeden element do drugiego, ale tylko sprawdzanie bool:

BOOST_CHECK (first == second); 

Wybierz preferowaną metodę.


Moja preferencja jest pierwsza, ale jej realizacja jest zaskakująco trudna. Jeśli po prostu zdefiniujesz operator<< w zasięgu globalnym, nie będzie działać. Myślę, że powodem tego jest problem z rozpoznawaniem nazw. Jedną z popularnych propozycji naprawienia tego jest umieszczenie operator<< w przestrzeni nazw . Działa to, przynajmniej w praktyce na niektórych kompilatorach, ale nie podoba mi się to, ponieważ Standard zabrania dodawania czegokolwiek do przestrzeni nazw std.

Lepszą metodą, jaką znalazłem, jest wdrożenie niestandardowej specjalizacji szablonów klas dla danego typu. print_log_value jest szablonem używanym do użycia w szablonie klasy przez elementy wewnętrzne Boost.Test w celu wywołania prawidłowego operator<< dla określonego typu. Deleguje na operator<<, aby wykonać ciężki lifting. Specjalizacja print_log_value dla twoich niestandardowych typów jest oficjalnie wspierana przez Boost [potrzebne źródło] i jest realizowana w ten sposób.

Zakładając, że typ nazywany Timestamp (jest w moim kodu), najpierw zdefiniować globalnego wolnego operator<< dla Timestamp:

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

...a następnie dostarczyć specjalizację print_log_value za to, delegując do operator<< po prostu zdefiniowane:

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

Rozumiem, dziękuję, zrobię na razie proste boost_check(), ponieważ naprawdę muszę przetestować niektóre rzeczy dość szybko, ale rozważę inne metody na później. – Ioana

+3

Przynajmniej z boost_1.61.00 musisz określić szablon 'print_log_value' wewnątrz' boost :: test_tools :: tt_detail'. – Ace7k3

+0

Dzięki za aktualizację, @ ​​Ace7k3 –

4

oparciu o odpowiedź John Dibling „s szukałem sposobu, aby zrzucić wartości całkowite w hex zamiast przecinku wymyśliłem ten podejście:

// 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 

teraz w moim testowym przypadku jednostki, mogę włączyć/wyłączyć hex, ustawiając wartość bool globalnego statycznego, na przykład:

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()); 
} 

I uzyskać zachowanie szukałem:

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] 

Alternatywnie, można użyć menedżera kontekstowego:

{ 
    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); 
} 

i wyjście hex będzie używany tylko w tym bloku:

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: Właśnie to zobaczyłem i jestem fanem. Coś takiego jest bardzo przydatne przy próbie zrzucenia rzeczy takich jak pakiety UDP. –

2

To jest uzupełnienie znakomitego answer from John Dibling. Wydaje się, że problemem musi być operator wyjściowy w odpowiedniej przestrzeni nazw. Jeśli więc zdefiniujesz globalną wartość wyjściową operator<<, możesz uniknąć tego błędu (przynajmniej w Visual Studio 2015, aka vc14 i doładowanie 1.60), definiując inny w przestrzeni nazw boost::test_tools::tt_detail, który przekazuje dalej operatorowi globalnemu. To drobne ulepszenie pozwala uniknąć dziwnej i bardziej szczegółowej specjalizacji klasy print_log_value. Oto co zrobiłem:

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 

Podczas Minęły trzy lata odkąd to pytanie i odpowiedź zostały wysłane, nie widziałem tego jasno omówione w Boost.Test documentation.

1

Istnieje czysty sposób uruchamiania funkcji Zwiększ 1.64, aby rejestrować typy zdefiniowane przez użytkownika za pomocą punktów dostosowywania . Pełną dokumentację tej funkcji można znaleźć pod adresem here.

Przykład z dokumentacji podano poniżej. Chodzi o to, aby zdefiniować funkcję boost_test_print_type dla typu, który chcesz wydrukować, i doprowadzić tę funkcję do przypadku testowego (znaleziony przez 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); 
} 
Powiązane problemy