我如何支持在 C++ 的异常处理宏中使用流?

How can I support using streams in an exception handling macro in C++?

我有一个像这样的小错误函数:

    template<typename ErrorType>
    void throwError(const std::string &file,
                    const std::string &function,
                    unsigned int line,
                    const std::string &msg = "") {
        std::ostringstream errMsg;
        errMsg << file << ":" << line << ":" << function << ":"
               << "\nError: " << msg << std::endl;
        std::cerr << errMsg.str();
        throw ErrorType(errMsg.str());
    }

然后我有一些使用函数的宏:

#define INVALID_ARGUMENT_ERROR(msg) throwError<std::invalid_argument>(__FILE__, __func__, __LINE__, msg)
#define LOGIC_ERROR(msg) throwError<std::logic_error>(__FILE__, __func__, __LINE__, msg)

所以我能做到:

if (condition == bad)
    LOGIC_ERROR("you did a bad");

但是当我想在错误消息中添加附加信息时,这很不方便,例如数字的值。

修改此函数以使其能够使用流而不是字符串的好方法是什么?所以我希望能够做到:

if (condition == bad)
    LOGIC_ERROR("you did a bad because condition \"" << condition << " != " << bad);

我试过将 std::string string msg 更改为 std::ostringstream,但不起作用。

如果您愿意稍微更改语法,则可以使用简单的辅助宏。给定您当前的函数模板...

template<typename ErrorType>
void throwError(const std::string &file,
                const std::string &function,
                unsigned int line,
                const std::string &msg = "")
{
    std::ostringstream errMsg;
    errMsg << file << ":" << line << ":" << function << ":"
           << "\nError: " << msg << std::endl;
    std::cerr << errMsg.str();
    throw ErrorType(errMsg.str());
}

然后您可以定义...

#define THROW_HELPER(ex_type)                                           \
    for (std::stringstream ss; true; throwError<ex_type>(__FILE__, __func__, __LINE__, ss.str())) \
        ss

#define INVALID_ARGUMENT_ERROR THROW_HELPER(std::invalid_argument)
#define LOGIC_ERROR            THROW_HELPER(std::logic_error)

这些可以用作,例如...

LOGIC_ERROR << "extra messages go here";

请注意,目前在抛出异常的过程中创建了两个单独的 std::stringstream 实例,因此显示的代码可能应该 'compacted' 一点以防止 [留作练习 :-) ].

我提出一个不同的选择:libfmt!

#define LOGIC_ERROR(...) \
    throwError<std::logic_error>(__FILE__, __func__, __LINE__, fmt::format(__VA_ARGS__))

用法:

if (condition == bad)
    LOGIC_ERROR("you did a bad because condition {} != {}", condition, bad);

这种方法的缺点是如果格式字符串无效,fmt::format 将抛出异常。您可以使用 FMT_STRING() 宏在 compile-time 处检查它:

#define LOGIC_ERROR(string) \
    throwError<std::logic_error>(__FILE__, __func__, __LINE__, string)

#define LOGIC_ERROR_P(string, ...) \
    throwError<std::logic_error>(__FILE__, __func__, __LINE__, \
    fmt::format(FMT_STRING(string), __VA_ARGS__))

我在这里创建了两个版本的 LOGIC_ERROR,有和没有额外参数。根据参数的数量,可以在这两者之间进行单个宏调度,但这留给 reader.

作为练习。