如何在不将字符串对象嵌入自定义异常类的情况下重新格式化错误消息

How to approach to reformat error messages without embedding string object in custom exception classes?

本文关键字:情况下 消息 错误 格式化 自定义异常 字符串 对象      更新时间:2023-10-16

我已经阅读了以下问题的答案->

c++异常:抛出std::string

c++异常类设计

我的要求是捕获异常,并允许一种机制来重新格式化错误消息,然后重新抛出异常。因此,我想到编写我的异常类,它将提供一个方法来在不同的捕获点重新格式化错误消息。为了使重新格式化更容易,我在myexception_base类中嵌入了一个字符串对象。

我还参考了这篇文章-> http://www.boost.org/community/error_handling.html,其中明确指出不要在异常类中嵌入字符串对象。

文章还引用了"Peter Dimov提出了一个很好的论点,即正确使用what()字符串是作为错误消息格式化程序表的键",但它没有进一步详细说明。

我已经写了如下代码,我的问题是-

1)如果我没有在异常类中嵌入字符串对象,它将使重新格式化what消息变得困难。请告诉我如何在不嵌入字符串对象的情况下实现我的需求在我的异常类?

2)如果我继续我目前的方法,嵌入一个字符串对象,并提供一个方法来格式化错误消息。我处理某些情况的方式是否正确?(问题1和问题2中错误信息没有被格式化)

问题1 -错误的异常被传播到捕获点?问题2 -错误信息没有得到格式化在某些情况下?Issue 1和Issue 2在下面代码的注释中提到。

请看看代码。我在代码中添加了很多注释,所以代码行数增加了,但如果我删除注释,它的代码行数就会减少。

#include <string>
#include <exception>
#include <iostream>
using namespace std;
class myexception_base : public exception {
private:
    string error_msg;
public:
    myexception_base(const char* str) : error_msg(str) {    
        // std::string here here can throw an exception while error_msg 
        //initialisation through MIL. 
        // myexception_base object will not be constructed 
        // and wrong exception will be  propagated to catch point(Issue 1)
    }
    const char* what() const throw() {  
        try {
            return error_msg.c_str();   
        } catch (...) {}
        // c_str can throw. 
        // I can't remove throw specification from what()
        // because what() in base std::exception has throw() specification  
        // In case c_str() throws an exception program will terminate
        // So to stop abnormal termination of program c_str 
        // is wrapped in a try catch block to ignore std::string exception
        // program will not terminate but what message is not reformatted
        // in such scenario (Issue 2)
    }
    void append_error_msg(const char* str) {
        error_msg.append(str);      
        // append can throw
        // Again error_msg will not be formatted (Issue 2)
    }
    ~myexception_base() throw() {
    }
};
void f();
void f2();
void f() {
    throw myexception_base("Error Msg1");
}
void f2() {
    //Some intermediate Catch point will reformat e.what() msg
    //by appending an error msg2
    try {
        f();
    }
    catch (myexception_base& e) {
        e.append_error_msg(": Error Msg2");
        throw;
    }
}

int main () {
    try {
        f2();
    }
    catch(const exception& e) {
        // Finally the exception is caught in the upper most layer
        // and proper error_msg is given to user
        cout<<e.what()<<"n";
    }
}

根据boost文档,在其生命周期内嵌入可能抛出的类并不是异常处理程序机制中使用的优秀候选者,因此我想建议您简单地使用旧的C处理字符串的好方法。这将需要一些额外的努力来启动和运行,但有了它们,你就消除了一个错误来源。

基本上有两种方法:

  1. 在myexception_base中,将error_msg声明为N个字符的数组。在append_error_msg中,仔细检查NEW字符串的长度(在追加之后)不大于N,如果不大于N,则使用strcat或其他C字符串处理方法之一。这种方法可能是好的,因为它不允许通过异常处理机制发生内存泄漏,但是您只能拥有有限长度的错误消息。

  2. 在myexception_base中,你将error_msg声明为char* (char指针)。在构造函数你calloc内存为它(不使用new,因为新的可能会抛出),并在append_error_message你realloc指针是新的大小(即。旧的大小+要附加的字符串。size + 1为0),然后memcpy到请求的位置(即。在新分配的字符串中旧字符串的末尾位置)。当然,不要忘记检查所有的分配/重新分配是否返回了一个有效值,否则您可能会遇到奇怪的行为。最后,但并非最不重要的是,为了避免经典的内存泄漏,在析构函数中释放char*的内存。

祝你好运!