2

在使用 gcc 6.2.0 的 Catch Unit Test v1.8.1 中,当测试失败时,我试图通过将向量传递给INFO(...)or来方便地输出向量的内容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命名空间并进入全局命名空间(通过注释 out )时,由于将向量传递给宏#define THIS_WORKS_BUT_EXTENDING_NAMESPACE_STD_IS_ILLEGAL,代码无法编译。CAPTURE()

根据Catch docs,我尝试用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

有没有适当的、合法的方式来做到这一点?

4

2 回答 2

0

我想我找到了一些比我之前给出的解决方案更好的解决方案:


解决方案1:

将 Catch 更新到 v1.8.2 或更高版本。通过一些快速测试,看起来 v1.8.2 增加了对std::vectorCAPTURE 宏的支持,而您无需付出任何额外的努力。在这种情况下,不需要重载operator<<for 。std::vector


解决方案2:

如果您由于某种原因无法更新到 Catch v1.8.2 或更新版本,此解决方案类似于我最初问题中提出的解决方案,但基于C++ 委员会成员 Jonathan Wakely 的回答进行了改进(谢谢!)。

他给出以下建议:

不要为您无法控制的类型重载运算符。

...

而是创建一个小型适配器类并为此定义运算符...

所以考虑到这一点:

#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
于 2017-10-03T05:12:13.950 回答
0

我想我找到了一个有效的答案。(编辑:请参阅其他答案以获得更好的解决方案。)

不是将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;
}

}

Catch 文档说要将重载放入与您的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 不是我的类型,并且它存在于 namespace 中std,所以我无法按照文档所说的去做。

那么是否可以将operator<<重载放到Catch命名空间中呢?它有效,但可以吗?如果我这样做,会发生坏事吗?文档确实说可以将重载toString放入 Catch 命名空间中,那么这是否也可以用于operator<<重载?

于 2017-03-09T05:40:29.077 回答