2013-07-10 1 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<<, который работает с вашим типом, не сравнивая один пункт к другому, но только проверка bool:

BOOST_CHECK (first == second); 

Выберите нужный метод.


Мое предпочтение - это первое, но осуществление, которое удивительно сложно. Если вы просто определяете operator<< в глобальной области, это не сработает. Я думаю, причина в том, что проблема с разрешением имен. Одно популярное предложение исправить это - положить operator<< в пространство имен std. Это работает, по крайней мере, на некоторых компиляторах, но мне это не нравится, потому что стандарт запрещает добавлять что-либо в пространство имен std.

Лучший метод, который я нашел, - это реализовать специальную специальную форму шаблона класса для вашего типа. print_log_value - это шаблон шаблона useb для внутренних объектов Boost.Test, чтобы на самом деле вызвать правильный 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); 
} 
};               
}} 
+0

Я понимаю, спасибо, сейчас я сделаю простой boost_check(), так как мне действительно нужно проверить некоторые вещи довольно срочно, но я рассмотрю другие методы позже. – Ioana

+3

По крайней мере, с boost_1.61.00 вы должны указать шаблон 'print_log_value' внутри' boost :: test_tools :: tt_detail'. – Ace7k3

+0

Спасибо за обновление, @ Ace7k3 –

4

На основании ответа John Dibling «s Я искал способ сбросить целые значения в HEX, а не десятичной, я пришел с этим подход:

// 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<<, вы можете избежать этой ошибки (по крайней мере, с Visual Studio 2015, aka vc14 и boost 1.60), определив еще один в пространстве имен boost::test_tools::tt_detail, который пересылается глобальному оператору. Эта небольшая настройка позволяет избежать странной и более подробной специализации класса 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); 
} 
Смежные вопросы