В Catch Unit Test v1.8.1, с gcc 6.2.0, я пытаюсь удобно выводить содержимое вектора при сбое теста, передавая вектор в INFO(...)
или же CAPTURE(...)
, Для этого я перегружаю оператор вставки потока:
#include <Catch/single_include/catch.hpp>
#include <vector>
#include <iostream>
#define THIS_WORKS_BUT_EXTENDING_NAMESPACE_STD_IS_ILLEGAL
#ifdef THIS_WORKS_BUT_EXTENDING_NAMESPACE_STD_IS_ILLEGAL
namespace std {
#endif
std::ostream& operator << ( std::ostream& os, const std::vector<int>& v ) {
for ( const auto& e : v ) {
os << e << " ";
}
return os;
}
#ifdef THIS_WORKS_BUT_EXTENDING_NAMESPACE_STD_IS_ILLEGAL
} //namespace std
#endif
int some_operation_on_vector( const std::vector<int>& v ) {
return 1;
}
SCENARIO( "some scenario" )
{
GIVEN( "a vector" )
{
const auto the_vector = std::vector<int>{ 1, 2, 3, 4, 5 };
WHEN( "some result is calculated from the vector" )
{
const auto actual_result = some_operation_on_vector( the_vector );
THEN( "the result should be correct. If not, print out the vector." )
{
const auto expected_result = 0;
CAPTURE( the_vector ); // <--------
//^^^^
//How do I legally make this work?
REQUIRE( expected_result == actual_result );
}
}
}
}
Если я (незаконно) продлю std
Пространство имен, как указано выше, затем он работает, и я вижу желаемый результат:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
catchtestexample is a Catch v1.8.1 host application.
Run with -? for options
-------------------------------------------------------------------------------
Scenario: some scenario
Given: a vector
When: some result is calculated from the vector
Then: the result should be correct. If not, print out the vector.
-------------------------------------------------------------------------------
ExampleTest.cpp:91
...............................................................................
ExampleTest.cpp:95: FAILED:
REQUIRE( expected_result == actual_result )
with expansion:
0 == 1
with message:
the_vector := 1 2 3 4 5
===============================================================================
test cases: 1 | 1 failed
assertions: 1 | 1 failed
Но чтобы попытаться быть законным, когда я пытаюсь operator <<
перегрузка из std
пространство имен и в глобальное пространство имен (путем комментирования #define THIS_WORKS_BUT_EXTENDING_NAMESPACE_STD_IS_ILLEGAL
) код не компилируется из-за передачи вектора в CAPTURE()
макро.
В соответствии с Поймать документы, Я пытался заменить operator <<
перегрузка с Catch::toString
перегрузка:
#include <string>
#include <sstream>
namespace Catch {
std::string toString( const std::vector<int>& v ) {
std::ostringstream ss{};
for ( const auto& e : v ) {
ss << e << " ";
}
return ss.str();
}
}
или с Catch::StringMaker
специализация:
#include <string>
#include <sstream>
namespace Catch {
template<> struct StringMaker<std::vector<int>> {
static std::string convert( const std::vector<int>& v ) {
std::ostringstream ss{};
for ( const auto& e : v ) {
ss << e << " ";
}
return ss.str();
}
};
}
но в любом случае тест по-прежнему не компилируется из-за передачи вектора в CAPTURE()
макро.
Документы Catch говорят, чтобы поставить operator <<
перегрузка в то же пространство имен, что и ваш тип, но std::vector
это не мой тип, и положить эту перегрузку в пространство имен std
незаконно
Но единственный способ, которым я смог найти, чтобы получить CAPTURE()
(или же INFO()
, или же WARN()
и т. д.) принять std::vector
аргумент в том, чтобы незаконно поставить operator <<
перегрузка в пространство имен std
,
Есть ли правильный, законный способ сделать это?
Я думаю, что нашел ответ, который работает. (РЕДАКТИРОВАТЬ: см. другой ответ для лучших решений.)
Вместо того, чтобы положить operator <<
перегрузка в std
пространство имен, поместив его в Catch
Пространство имен компилируется и дает желаемое поведение:
namespace Catch {
std::ostream& operator << ( std::ostream& os, const std::vector<int>& v ) {
for ( const auto& e : v ) {
os << e << " ";
}
return os;
}
}
Поймать документы скажи поставить operator <<
Перегрузите в то же пространство имен, что и ваш тип:
оператор << перегрузка для std :: ostream
Это стандартный способ преобразования строк в C ++, и есть вероятность, что вы уже можете предоставить его для своих собственных целей. Если вы не знакомы с этой идиомой, то она предполагает написание бесплатной функции вида:
std::ostream& operator << ( std::ostream& os, T const& value ) { os << convertMyTypeToString( value ); return os; }
(где
T
это твой тип иconvertMyTypeToString
здесь вы будете писать любой код, необходимый для того, чтобы ваш тип печатался (это не обязательно должно быть в другой функции).Вы должны поместить эту функцию в то же пространство имен, что и ваш тип. [Акцент мой]
В качестве альтернативы вы можете написать его как функцию-член:
std::ostream& T::operator << ( std::ostream& os ) const { os << convertMyTypeToString( *this ); return os; }
Но так как std :: vector не мой типа, и он живет в пространстве имен std
Я не могу сделать то, что говорят документы.
Так можно ли ставить operator <<
перегрузка в Catch
пространство имен вместо? Это работает, но это нормально? Произойдут ли плохие вещи, если я это сделаю? Документы говорят, что это нормально, чтобы перегрузить toString
в пространство имен Catch, так что это делает это нормально для operator <<
перегрузки тоже?
Я думаю, что нашел некоторые решения лучше, чем тот, который я дал ранее:
Обновите Catch до v1.8.2 или новее. Из некоторых быстрых тестов, похоже, v1.8.2 добавлена поддержка std::vector
в макросах CAPTURE, без каких-либо дополнительных усилий с вашей стороны. перегрузка operator <<
за std::vector
не требуется в этом случае.
Если по какой-либо причине вы не можете выполнить обновление до Catch v1.8.2 или новее, это решение аналогично предлагаемому решению в моем первоначальном вопросе, но с улучшениями, основанными на этот ответ от члена комитета C ++ Джонатана Уэйкли (Спасибо!).
Он дает следующий совет:
Не перегружайте операторы для типов, которые вы не контролируете.
…
Вместо этого создайте крошечный класс адаптера и определите оператор для этого …
Итак, имея в виду:
#include <Catch/single_include/catch.hpp>
#include <vector>
#include <iostream>
template <typename T> struct PrintableVector {
const std::vector<T>& vec_;
};
template <typename T>
PrintableVector<T> makePrintable( const std::vector<T>& vec ) {
return PrintableVector<T>{ vec };
}
template <typename T>
std::ostream& operator << ( std::ostream& os, const PrintableVector<T>& printableVec ) {
for ( const auto& e : printableVec.vec_ ) {
os << e << " ";
}
return os;
}
int some_operation_on_vector( const std::vector<int>& v ) {
return 1;
}
SCENARIO( "some scenario" )
{
GIVEN( "a vector" )
{
const auto the_vector = std::vector<int>{ 1, 2, 3, 4, 5 };
WHEN( "some result is calculated from the vector" )
{
const auto actual_result = some_operation_on_vector( the_vector );
THEN( "the result should be correct. If not, print out the vector." )
{
const auto expected_result = 0;
CAPTURE( makePrintable( the_vector ) );
REQUIRE( expected_result == actual_result );
}
}
}
}
Это компилирует и запускает на Catch v1.8.1, и дает следующий вывод:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
catchtestexample is a Catch v1.8.1 host application.
Run with -? for options
-------------------------------------------------------------------------------
Scenario: some scenario
Given: a vector
When: some result is calculated from the vector
Then: the result should be correct. If not, print out the vector.
-------------------------------------------------------------------------------
main.cpp:43
...............................................................................
main.cpp:47: FAILED:
REQUIRE( expected_result == actual_result )
with expansion:
0 == 1
with message:
makePrintable( the_vector ) := 1 2 3 4 5
===============================================================================
test cases: 1 | 1 failed
assertions: 1 | 1 failed