2013-07-10 12 views
6

Ho scritto questa classe davvero banale in modo che sia chiaro che cosa il mio problema è:BOOST_CHECK fallisce la compilazione operatore << per i tipi personalizzati

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

Ora, se io definisco un primo (1) e un secondo (1), mi sembra naturale che BOOST_CHECK_EQUAL (primo, secondo) debba passare. Tuttavia, ho ottenuto 50 errori quando provo a fare questo, il primo suona come: no matematica per operatore < < in ostr < < t che è da qualche parte nel codice boost ... Altri test funzionano bene, confrontando tipi noti o addirittura puntatori, ma c'è qualcosa di diverso che sembra accadere con oggetti di classe.

+2

Quali sono stati gli errori? – Dennis

+0

Non si compila ... E sto lavorando in Codeblocks, quindi non posso copiare e incollare gli errori. E tutti sono nel file test_tools.hpp e non capisco veramente di cosa si tratti (ho menzionato il primo nel post). Dovrei dare più esempi? – Ioana

+0

Ho compilato con gcc e vorrei incollare gli errori ma è troppo lungo 11400 caratteri per un commento. – Ioana

risposta

17

Esistono tre modi per identificare il problema con operator<<.

Il primo modo è fornire un operator<< per il proprio tipo. Ciò è necessario perché quando lo boost_check_equal non riesce, registra anche l'errore chiamando operator<< con gli oggetti. Vedere l'appendice dettagliato dopo l'interruzione per vedere come questo è effettivamente realizzato. È più difficile di quanto possa sembrare.

Il secondo modo è di non eseguire la registrazione che ho appena menzionato. È possibile farlo da #definine ing BOOST_TEST_DONT_PRINT_LOG_VALUE. Per disattivare la registrazione per un solo esame, si potrebbe circondare la prova in questione con questo #define, allora #undef immediatamente:

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

Il terzo modo è quello di eludere la necessità di un operator<< che funziona con il tipo, non confrontando un elemento ad un altro, ma solo controllando un bool:

BOOST_CHECK (first == second); 

selezionare il metodo preferito.


La mia preferenza è la prima, ma l'implementazione è sorprendentemente impegnativa. Se si definisce semplicemente uno operator<< nell'ambito globale, non funzionerà. Penso che la ragione di ciò sia a causa di un problema con la risoluzione dei nomi. Un suggerimento popolare per risolvere questo problema è quello di inserire lo operator<< nello spazio dei nomi std. Questo funziona, almeno in pratica su alcuni compilatori, ma non mi piace perché lo standard proibisce di aggiungere qualcosa allo spazio dei nomi std.

Un metodo migliore che ho trovato è quello di implementare una specializzazione di modello di classe print_log_value personalizzata per il tipo. print_log_value è un modello di classe useb dagli interni di Boost.Test per chiamare effettivamente il operator<< corretto per il tipo specificato. Delega a un operator<< per fare il sollevamento pesi. Specializzare print_log_value per i tuoi tipi personalizzati è ufficialmente supportato da Boost [citazione necessaria], ed è realizzato in tal modo.

Assumendo che il tipo è chiamato Timestamp (è nel mio codice), in primo luogo definire un libero operator<< globale per Timestamp:

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

...e quindi fornire il print_log_value specializzazione per esso, delegando al operator<< appena definito:

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

Capisco, grazie, farò un semplice boost_check() per ora, dato che ho davvero bisogno di testare alcune cose con urgenza, ma considererò gli altri metodi per dopo. – Ioana

+3

Almeno a partire da boost_1.61.00 devi specificare il modello 'print_log_value' in' boost :: test_tools :: tt_detail'. – Ace7k3

+0

Grazie per l'aggiornamento, @ Ace7k3 –

4

in base alla risposta John Dibling s' che stavo cercando un modo per eseguire il dump valori interi in esadecimale invece che decimali sono arrivato fino a questo approccio:

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

Ora, nel mio caso di test di unità, posso accendere/spegnere esagonale impostando il valore bool statico globale, ad esempio:

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

E ho il comportamento che cercavo:

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] 

In alternativa, è possibile utilizzare il gestore di contesto:

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

E uscita esadecimale saranno utilizzati solo in quel blocco:

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: Ho appena visto questo ora, e sono un fan. Qualcosa di simile è molto utile quando si tenta di scaricare cose come i pacchetti UDP. –

2

Questo è un supplemento all'eccellente answer from John Dibling. Il problema sembra essere che ci deve essere un operatore di output nel giusto spazio dei nomi. Pertanto, se si dispone di un'uscita globale operator<< definita, è possibile evitare questo errore (almeno con Visual Studio 2015, ovvero vc14 e boost 1.60) definendo un altro nello spazio dei nomi boost::test_tools::tt_detail che inoltra all'operatore globale. Questo piccolo aggiustamento consente di evitare una specializzazione strana e più prolissa della classe print_log_value. Ecco quello che ho fatto:

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 

Mentre è stato tre anni da quando questa domanda e la risposta sono stati pubblicati, non ho visto questo discusso chiaramente nel Boost.Test documentation.

1

C'è un modo pulito che avvia Boost 1.64 per registrare i tipi definiti dall'utente tramite i punti di personalizzazione . La documentazione completa di questa funzione può essere trovata here.

Un esempio della documentazione è riportato di seguito. L'idea è di definire la funzione boost_test_print_type per il tipo che si desidera stampare, e per portare questa funzione nel caso di test (che si trova via 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); 
} 
Problemi correlati