【问题标题】:Return a dynamic string from std::exception's `what`从 std::exception 的 `what` 返回一个动态字符串
【发布时间】:2010-04-10 16:43:39
【问题描述】:

此时我确信我应该为我所有的异常抛出需求创建std::exception 的子类。现在我正在研究如何覆盖what 方法。

我所面临的情况,如果字符串what 返回是动态的,那将非常方便。例如,有些代码会解析 XML 文件,在错误消息中添加位置或行号对我很有用。

我正在尝试关注Boost Exception handling guidelines

我想知道的:

  • what 返回一个const char *,这意味着任何捕手都可能不会释放字符串。所以我需要其他地方来存储结果,但那会在哪里呢? (我需要线程安全。)

  • what 还在其签名中包含throw()。虽然我可以阻止我的what 抛出任何东西,但在我看来,这种方法确实不适合任何过于动态的东西。如果what 不是正确的地方,那我应该在哪里做呢?


从我目前得到的答案看来,实现这一点的唯一方法是将字符串存储在异常中。 Boost 指南建议不要这样做,这让我很困惑,因为 std::runtime_error 就是这样做的。

即使我要使用 C 字符串,我也必须使用静态大小的缓冲区,或者进行内存管理,这也可能会失败。 (我想知道这是否真的是std::string 的复制构造函数中唯一可能出错的地方。这意味着我不会使用动态分配的 C 字符串获得任何东西。)

还有其他选择吗?

【问题讨论】:

  • 添加了 Boost 指南点,如果不适合您,请重新编辑。

标签: c++ exception constants


【解决方案1】:

我的异常类通常除了构造函数之外什么都没有,请按照以下思路查看:

class MyEx: public std::runtime_error 
{
public: 
    MyEx(const std::string& msg, int line): 
        std::runtime_error(msg + " on line " + boost::lexical_cast<string>(line)) 
    {} 
}; 

一个任意示例,但它是处理管理what() 消息的基类。

但是如果你愿意,你也可以只分配异常对象的基础部分,在你将消息放在构造函数体中之后。

#include <stdexcept>
#include <string>
#include <sstream>

class MyEx: public std::runtime_error
{
public:
    MyEx(const std::string& msg, int line):
        std::runtime_error("")
    {
        std::stringstream ss;
        ss << msg << " on line " << line;
        static_cast<std::runtime_error&>(*this) = std::runtime_error(ss.str());
    }
};

#include <iostream>
int main()
{
    try {
        throw MyEx("Evil code", __LINE__);
    }
    catch (const std::exception& e) {
        std::cout << e.what() << '\n';
    }
}

但是,关于 boost 的指导方针,也许您应该注意一点,数字数据(位置和线)可能最好通过其他方法以数字形式提供。指导方针说不要担心what() 消息。

【讨论】:

  • 更令人担忧的是,我发现std::runtime_error 存储了一个字符串。那么标准库本身与 Boost 准则相矛盾?
  • Boost 作者担心格式化或复制字符串会引发(内存不足?)错误的(远程)可能性。异常对象总是在抛出时被复制。
【解决方案2】:

Boost 的指导方针似乎基于两个假设:复制异常对象可能会引发另一个异常,并且 what() 字符串不是一个健壮或可靠的工具。如果您正在编写一个将在各种环境中广泛使用的库,那么这些都是有效的担忧。如果您对如何使用异常有更好的了解,您可以判断这些担忧是否合理或无事生非。编程就是一系列权衡,而对 Boost 开发人员有意义的权衡可能不适用于您。

【讨论】:

    【解决方案3】:

    好吧,没问题,您可以简单地实现派生异常类的构造函数来格式化您将从 what() 返回的字符串。释放你在析构函数中使用的缓冲区。

    【讨论】:

    • 这似乎与 gf 的答案相似,还是您的意思是常规 C 字符串?是否有可能在捕获之前释放字符串,因为运行时将异常作为值传递?
    • 不确定我是否遵循,可以释放什么字符串?您必须在存储指针的异常类中实现复制构造函数。
    【解决方案4】:

    我接受 UncleBens 的回答,因为从技术上讲,我认为它是对我最初问题的最正确和最完整的回答。

    作为参考,我实际上选择了一个不同的解决方案,并完全停止使用what。我已经重构了我现在必须使用类似这样的代码作为基本异常类:

    struct Exception : public virtual std::exception
    {
      virtual const char* what() const throw()
      {
        try {
          return typeid(this).name();
        }
        catch (const std::exception& e) {
          return "<unknown exception>";
        }
      }
    
      // Extended description; may throw.
      virtual void describe(std::ostream& out) const = 0;
    };
    

    基本上只是用我能找到的最有意义的东西填写what,而不用在其他任何地方打扰它。我想我会看看这个票价如何。

    【讨论】:

      猜你喜欢
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多