catch站点中异常的常见用法是什么

What are the common usage of exceptions at catch site?

本文关键字:常见 用法 是什么 异常 站点 catch      更新时间:2023-10-16

我对异常处理的理解非常有限。虽然我发现抛出异常很容易(或者我可以使用expected<T>将其打包以供以后使用),但我对如何处理异常知之甚少。

目前我的知识仅限于

  • 清理我自己的资源,并重新抛出要在适当位置处理的异常。例如

    ptr p = alloc.allocate(n);
    try
    {
       uninitialized_copy(first,last,p);//atomic granularity, all or none
    }
    catch(...)
    {
        alloc.deallocate(p,n);
        throw;
    }
    

但我想,这可以在RAII模式中等效地转换为

alloc_guard<ptr> p{alloc.allocate(n)};
uninitialized_copy(first,last,p.get());
p.commit();
  • 在顶层捕获异常,compose&打印一条漂亮的消息并退出。例如

    int main(int argc,char** argv)
    {
       try
       {
           app_t the_app(argc,argv);
           the_app.run();
       }
       catch(std::runtime_error& e)
       {
          //instead of what, I can also compose the mesage here based on locale.
          std::cout<<e.what()<<std::endl;
       }
    }
    

因此,我所做的只是在顶级函数(如main)中捕获异常并打印适当的消息并关闭。

在使用各种外部库作为实现的后端使用一组漂亮的API实现库时,我意识到第三方库异常是我的API规范的一部分,因为它们跨越了我的库边界并进入了用户代码!

因此,我的库API将我正在使用的外部库(每个库都有自己的异常层次结构)中的所有异常泄露给了用户代码。

这就引出了我的问题,当我发现任何异常时,能做些什么

更具体地说,

  • 我是否可以将捕获的异常从外部库转换为我自己的异常,并以通用的方式抛出它(比如第三方库异常层次结构和我的异常API之间的映射以mpl::map的形式提供)
  • 我能做一些比打印消息/调用堆栈更有用的事情吗?比如用不同的输入参数在throw站点恢复函数(比如当我得到file_not_founddisk_error时,用不同的文件重新运行函数)
  • 还有其他值得了解的模式吗

感谢

除了nogard所说的之外,我想添加以下内容:

  1. 例外情况应用于特殊情况。不该发生的事情
  2. 如果遇到异常,请至少将其记录在某个位置。这可能有助于您查找错误
  3. 请尝试在捕获异常时解决错误
  4. 如果这不可能,请尝试保持一致状态,以便应用程序可以继续
  5. 如果这不可能——考虑优雅地终止
  6. 通知用户发生了异常情况

最后的建议-保持错误处理的一致性。这包括将异常从第三方库转换到您的异常层次结构。


评论答案:

2) 异常应该包含有关出现错误的信息。这可能只是类型或一些附加信息。在客户处记录这些信息可以让您获得更多的信息,除了客户告诉您的内容之外,还可以了解实际出了什么问题。也许他滥用了你的应用程序,发现了另一个用例,或者你只是有一个bug(例如,一个未初始化的变量)。但有了这些额外的信息,你就有了出错的位置和一些出错的信息。这有助于您推断错误的来源,从而找到错误。

3) 这实际上取决于正在发生的错误。例如,您试图访问一个不存在的配置文件-->您创建了一个具有默认值的新配置文件。客户端试图打开一个数据库进行写访问,但它受到写保护。您拒绝打开,返回到有效状态,并告诉客户端数据库是写保护的。内存不足,无法继续?记录这一点(注意-您没有多余的内存,所以您的日志记录应该已经为这个用例预先保留了一些内存),并优雅地关闭应用程序。如果可能的话,也许可以通知客户。

关于来自其他库的代码:没有其他方法可以检查对另一个库的每个函数调用是否返回异常并捕获它们。一旦被捕获,您可以将信息从该异常转移到您的异常中,并抛出该异常(或以其他方式解决它)

这是一个非常大的主题。

  1. 我怀疑你是否能轻易地将第三方的例外转化为你自己的例外,而且就我个人而言,我认为没有必要实施这种行为。既然第三方库是您的实现的一部分,它不向公共API公开,为什么要公开它的所有异常(甚至通过一些映射)?如果有一天你坚持使用另一个实现相同内容的第三方库,你想重新设计整个异常层次结构吗?我想不是。您的库的API一定不是脆弱的,所以我建议不要将外部异常映射到您自己的异常。

  2. 您可以通过以下方式将第三方例外映射到您的层次结构:

    • 什么都不要包。我的意思是,你不必因为第三个库就抛出任何东西。你可以捕捉并处理这个异常,或者返回错误代码,或者适当地更改状态。还有许多其他的可能性,而不是总是重新思考。

    • 您不必对所有第三方例外情况进行一对一翻译。如果您在内部使用库AAA,那么您可以有一个AAAException,表示来自该库的许多异常。

  3. 有价值的了解:总是通过常量引用捕获异常:

    catch (const exception & ex)

这个题目很大,我希望我的答案能帮助你理解它

评论答案:

  1. 如果我不将第三方异常映射到我自己的API(不必是一对一),它们会泄漏到客户端代码-不,它们不会,这就是重点!您必须在库中捕获它们,然后决定如何处理捕获的异常:抛出自己的异常、返回错误代码、通知客户端侦听器、记录错误等

    try {
        3rdpatry.call();
    } catch (const 3rdpartyException & ex) {
        // throw YourException(ex.what());
        // listener.notify(some_error)
        // return some_code
    }
    
  2. 通过const引用捕获根本不是为了阻止切片。这里有很好的讨论可以解释这一点。

对于较大的应用程序来说,顶级捕获通常是不够的。当您可以采取措施时,您需要捕捉异常,但处理异常的方法主要只有几种:

  1. 如果可以的话恢复。-(例如:检查更新->无法打开网络连接异常->忽略并立即不下载更新。)
  2. 告诉用户选择如何恢复。(例如:保存文件->无法创建文件异常->告诉用户选择不同的文件名或取消)
  3. 登录并退出。这是最顶层的包罗万象的场景