【问题标题】:How can I support using streams in an exception handling macro in C++?如何支持在 C++ 的异常处理宏中使用流?
【发布时间】:2020-09-26 07:53:20
【问题描述】:

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

    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,但不起作用。

【问题讨论】:

  • 您可以使用std::to_string()将条件转为字符串,并使用+运算符将分片的字符串拼接成一个字符串。

标签: c++ exception error-handling macros


【解决方案1】:

如果您愿意稍微更改语法,则可以使用简单的辅助宏。鉴于您当前的功能模板...

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 实例,因此显示的代码可能应该“压缩”一点以防止这种情况发生 [留作练习 :-)]。

【讨论】:

    【解决方案2】:

    我提出一个不同的选择: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() 宏检查它:

    #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,带有和不带有额外参数。根据参数的数量,可以在这两者之间进行单个宏调度,但这留给读者作为练习。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-20
      • 1970-01-01
      • 1970-01-01
      • 2011-06-05
      • 2019-12-16
      • 1970-01-01
      • 2021-08-05
      • 2016-03-25
      相关资源
      最近更新 更多