Catch2 迫使我在异常中添加对 std::string 的强制转换,这会产生其他问题吗?

Catch2 forces me to add a cast to std::string to my exceptions, can this create other issues?

本文关键字:转换 问题 其他 异常 string std 添加 Catch2      更新时间:2023-10-16

我正在使用 Catch2 来编写我的单元测试。

我想做的一件事是确保我捕获了正确的异常。在许多情况下,我都会抛出相同的异常,因此仅仅知道我正在捕获std::logic_error并不能证明确实捕获了异常的特定实例。

Catch2 为此提供了REQUIRE_THROWS_MATCHES()宏。

以下是我如何将其与Equals匹配器一起使用的示例:

CATCH_REQUIRE_THROWS_MATCHES(
std::make_shared<advgetopt::getopt>(
options_environment
, sub_argc
, sub_argv)
, advgetopt::getopt_exception_logic
, Catch::Matchers::Equals(
"section "invalid::name" includes a section separator (::) in ""
+ options_filename
+ "". We only support one level."));

只有这样不会编译,除非我的异常中有强制转换运算符。在这种情况下,这很容易,因为我有自己的例外。但我想知道为什么 Catch2 的作者想到使用强制转换来std::string而不是使用what()函数。

这是我当前的基类异常定义:

class logic_exception_t
: public std::logic_error
, public exception_base_t
{
public:
explicit                    logic_exception_t( std::string const & what, int const stack_trace_depth = STACK_TRACE_DEPTH );
explicit                    logic_exception_t( char const *        what, int const stack_trace_depth = STACK_TRACE_DEPTH );
virtual                     ~logic_exception_t() override {}
virtual char const *        what() const throw() override;
operator std::string () const;
};

以下是operator std::string () const函数:

logic_exception_t::operator std::string () const
{
return what();
}

有没有另一种方法可以满足 Catch2 要求并允许将异常转换为std::string而无需创建强制转换运算符?我只是不喜欢有一个可能会导致其他问题的演员阵容。

注意:我试图明确演员表,但 Catch2 也不喜欢它。它只是将异常传递给需要std::string的函数。

我今天遇到了同样的问题;有一个半途而废的解决方案,以Message匹配器的形式出现,记录在 Catch2 文档中。

REQUIRE_THROWS_MATCHES(fn(), SomeException, Message("Complete exception message"));

但是,我希望有一个类似Contains的功能。鉴于实现Message,实现它是微不足道的:

#include <catch2/matchers/catch_matchers.hpp>
#include <exception>
#include <string>
#include <string.h>
class ExceptionMessageContainsMatcher final : public MatcherBase<std::exception>
{
std::string m_messagePart;
public:
ExceptionMessageContainsMatcher(const std::string &messagePart) 
: m_messagePart{messagePart}
{}
bool match(const std::exception &e) const {
return ::strstr(e.what(), m_messagePart.data()) != nullptr;
}
std::string describe() const {
return "exception message does not contain "" + m_messagePart + '"';   
}
};
ExceptionMessageContainsMatcher MessageContains(const std::string &message) { 
return {message};
}

这允许您编写:

REQUIRE_THROWS_MATCHES(fn(), SomeException, MessageContains("part of the message"));

您实际上可以定义自己的观察程序,因此我决定编写一个观察程序,该观察程序将为其match()函数接受异常。这在没有铸造的情况下工作std::string

namespace Catch
{
namespace Matchers
{

class ExceptionWatcher
: public MatcherBase<std::exception>
{
public:
ExceptionWatcher(std::string const & expected_message)
: m_expected_message(expected_message)
{
}
/** brief Check whether we got a match.
*
* This function compares the expected string with the actual exception
* what() output.
*/
bool match(std::exception const & e) const override
{
return e.what() == m_expected_message;
}
/** brief Describe this matcher.
*
* This function produces a string describing what this matcher does.
*
* return The description of this matcher.
*/
virtual std::string describe() const override
{
return "compare the exception what() message with ""
+ m_expected_message
+ "".";
}
private:
std::string     m_expected_message = std::string();
};

inline ExceptionWatcher ExceptionMessage(std::string const & expeted_message)
{
return ExceptionWatcher(expeted_message);
}

}
// Matchers namespace
}
// Catch namespace