Отключить BOOST_CHECK для nullptr
С Boost Test ранее, чем версия 1.64, вы не можете сделать это:
SomeType* a_pointer = getPointer();
BOOST_CHECK_EQUAL(a_pointer, nullptr);
Это потому что nullptr_t
имеет неоднозначные перегрузки: проблема Boost #12778 и связанный вопрос SO. Как и в ответах на вопрос, это можно легко решить:
BOOST_CHECK(!a_pointer); // rely on boolean casting, or...
// cast nullptr away from nullptr_t
BOOST_CHECK_EQUAL(a_pointer, static_cast<SomeType*>(nullptr));
Тем не менее, если вы поддерживаете несколько буст-версий, BOOST_CHECK_EQUAL(a_pointer, nullptr)
проскользнуть на более новую платформу и сломать старые.
Одним из решений здесь является применение платформы CI с более старыми версиями Boost (что также полезно по другим причинам, особенно когда поддерживаемые версии Boost были расширены до 1.59, когда Boost Test 3 сильно изменился!).
Однако полагаться только на CI, чтобы поймать это, является большой задержкой в цикле OODA (по сравнению с ошибкой локального компилятора) и требует доступа к сети и простого, но раздражающего танца VCS для исправления простых изменений и повторной отправки задания.
Есть ли способ заставить его не скомпилироваться, даже если версия Boost в противном случае поддержит его?
1 ответ
В Boost Test это реализовано в коммите 229e71199 для v1.64, используя print_log_value
точка настройки:
template<>
struct BOOST_TEST_DECL print_log_value<std::nullptr_t> {
void operator()( std::ostream& ostr, std::nullptr_t t ) {
ostr << "nullptr";
}
};
Невозможно "отменить" функцию, определенную в другом модуле перевода (за исключением некоторых довольно неприятных хаков препроцессора). Так что на самом деле невозможно "повредить" функцию и вызвать сбой компиляции, если вы попытаетесь использовать функцию.
Однако у нас есть два лучших варианта: использовать тестовый метод Boost, чтобы избежать печати этого типа, или сделать это самостоятельно.
Избегайте печати nullptr_t
Вы используете существующую точку настройки Boost для предотвращения регистрации типа: BOOST_TEST_DONT_PRINT_LOG_VALUE
:
// in your common test header
BOOST_TEST_DONT_PRINT_LOG_VALUE( std::nullptr_t )
Что это делает, так как в Boost 1.59 это определить print_log_value
функция, которая ничего не делает:
#define BOOST_TEST_DONT_PRINT_LOG_VALUE( the_type ) \
namespace boost{ namespace test_tools{ namespace tt_detail{ \
template<> \
struct print_log_value<the_type > { \
void operator()( std::ostream&, the_type const& ) {} \
}; \
}}} \
До 1.59 ( commit bae8de14b) он определяется по-другому (не в tt_detail
для начала), но идея та же. Это означает, что он будет работать по крайней мере до 1,58 и ранее, используя этот макрос.
Однако, потому что в 1.64 print_log_value
была определена функция, если вы просто добавите вышеупомянутый макрос, вы получите ошибки переопределения начиная с 1.64: тот, который ничего не делает из DONT_PRINT
макрос и тот, который печатает "nullptr"
, Таким образом, вы можете защитить это с соответствующей версией Boost:
#if BOOST_VERSION < 106400
BOOST_TEST_DONT_PRINT_LOG_VALUE( std::nullptr_t )
#endif
Теперь он не печатает nullptr при Boost < 1.64 и печатает при 1.64+:
[ != 0xdeadbeef] // < 1.64, using BOOST_TEST_DONT_PRINT_LOG_VALUE
[nullptr != 0xdeadbeef] // 1.64 +, using built-in implementation
Это может быть достаточно, если вы не заботитесь о красоте регистрации в старых версиях Boost.
Сделай это сам
Вы также можете реализовать свой собственный print_log_value
пункт настройки. Тем не менее, обратите внимание, что пространство имен отличается до 1.59, и мы должны делать это только для < 1.64, так как, опять же, мы переопределим функцию:
// You don't need this bit if you don't support Boost Test <1.59.
#if BOOST_VERSION >= 105900
# define BOOST_TEST_PRINT_NAMESPACE_OPEN namespace boost { namespace test_tools { namespace tt_details {
# define BOOST_TEST_PRINT_NAMESPACE_CLOSE }}}
#else
# define BOOST_TEST_PRINT_NAMESPACE_OPEN namespace boost { namespace test_tools {
# define BOOST_TEST_PRINT_NAMESPACE_CLOSE }}
#endif
#if BOOST_VERSION < 106400
BOOST_TEST_PRINT_NAMESPACE_OPEN
template<>
struct print_log_value<nullptr_t> {
inline void operator()(std::ostream& os, nullptr_t const& p) {
os << "nullptr";
}
};
BOOST_TEST_PRINT_NAMESPACE_CLOSE
#endif // End <1.64 condition
Теперь он напечатает то же самое:
[nullptr != 0xdeadbeef] // < 1.64, using DIY implementation
[nullptr != 0xdeadbeef] // 1.64 +, using built-in implementation