【问题标题】:How can I reduce the debugging overhead from non-exceptional exceptions [closed]如何减少非异常异常的调试开销[关闭]
【发布时间】:2014-01-31 15:03:40
【问题描述】:

我们使用的 API 严重依赖异常来返回非异常结果。这方面的一个例子(在许多例子中)是为了确定用户是否在一组人中,我们必须尝试获取该组并解释由此产生的“无组”异常。此外,所有这些异常都是一种类型。

我们正在使用大量多线程的 C++11 开发一个大型而复杂的项目,此外,我们正在研究的领域涉及网络通信,因此有时我们必须同时调试多个实例。

我们的问题以及我的问题的基础是由于非异常异常对我们的工作流程的影响而出现的。我们不愿关闭 API 抛出的单一异常类型的第一次机会异常报告,因为这意味着如果我们调用 API 时编码人员错过了 try/catch 块,我们将展开主要和放松调用的上下文。如果我们将异常保留在简单的非异常行为上,例如上面示例中描述的行为,可能会导致多次中断(初始抛出和可能的一些重新抛出),我们只能验证这实际上是非通过查询堆栈(通常是非主线程的堆栈)来查找引发异常的 API 调用来查找异常异常。

我相信我们的用例并不是那么独特,以至于其他人不会遇到相同的工作流问题,所以我的问题是我们应该如何改变我们的调试过程,以便更好地应对非异常异常带来的问题,如上所述。

我们目前仅限于使用 Visual Studio(或可能是 WinDbg)进行调试。

【问题讨论】:

    标签: c++ exception visual-studio-debugging


    【解决方案1】:

    在非异常用例中抛出异常是完全错误的 imo。下一步:每个函数都是void,一切都通过抛出异常返回。

    据我了解,您使用的第三方 API 显示了这种奇怪的行为。以下是我在处理任何类型的“奇怪 API”时通常会考虑的内容,包括用愚蠢的宏污染一切的 C-API(是的,我看着你,WinAPI):

    1. 尽量避免表现异常的部分。在您的情况下:如果您可以调用非抛出检查,请执行此操作,即调用 if(hasGroup) getGroup; else ... 而不是 try {getGroup;} catch(X) {...}
    2. 不要让奇怪的东西波及所有代码,而是通过编写包装 API 来限制它。在您的情况下:编写一个捕获异常并将它们转换为正常返回值的包装器。这样,您的编码人员就不会忘记捕获正常情况下的异常(因为您的包装器不会抛出它们),并且如果您将包装器放在自己的库中,您可能可以在包装器 API 中使用另一个异常策略。您的 getGroup 问题的快速示例:

      //myAPIWrapper.h
      
      namespace myAPIWrapper {
      
        class Group;
      
        class User {
        public:
          boost::optional<Group> getGroup();
        };
      
      }
      
      
      //myAPIWrapper.cpp
      
      #include "OddAPI.h"
      
      namespace myAPIWrapper {
      
        boost::optional<Group> User::getGroup()
        {
          boost::optional<Group> theGroup;
      
          try {
            oddAPI::user& oddUser= unwrap(*this);
            oddAPI::group& oddGroup = oddUser.get_group();
            theGroup = wrap(oddGroup);
          }
          catch(oddAPI::exception&) {
          }
      
          return theGroup;
        }
      }
      

    【讨论】:

    • 不幸的是,没有替代调用,整个 API 使用异常,我给出的示例说明了这一点(即,知道你不在一个组中的唯一方法是引发异常) .包装器建议肯定是我们考虑过的,可能是最接近解决方案的东西,但需要大量样板代码,并且最终不会消除丢失异常的风险。我应该提一下,这不是一个小公司的小 API,我不能说出它们的名字,但我想你可以猜到。
    • 换一种说法,你的例子本质上就是我们正在做的,尝试在我们的代码和API之间的边界捕获异常,但总是有可能错过它们,所以它不允许我们关闭第一次机会异常,因此对我们的工作流程没有帮助。
    • “总是有可能错过它们”听起来您经常向 API 添加新调用。如果您使用该 API 可能需要的所有内容定义一个接口,并使用无异常包装器实现它,您将需要编写一次样板文件,并且不会有丢失异常的风险。如果您进一步为包装器提供详尽的单元测试(imo 总是有帮助),您可以确保它实际上 is 没有异常。也许应该由一个人负责维护包装器,因此其他人根本不必费心捕获异常。
    • 这基本上是我们必须做的,但我并没有真正看到单元测试的好处。每个函数都有一个 try/catch 或者没有。为了详尽地说“不打扰捕获异常”是足够安全的,您必须对 API 的工作方式有足够的了解,以确保您模拟了每个内部代码路径,即使那样 - 目的是什么?跨度>
    • 您不必模拟每个内部代码路径。这不是抽象、封装和包装的意义所在,也不是单元测试的意义所在。模拟您将要使用的包装器调用,以确保您的包装器按预期工作,包括它的非抛出行为。 是单元测试的意义所在,与你是否包装第三方 API 或编写业务逻辑类或其他无关。
    猜你喜欢
    • 2011-10-04
    • 2014-02-16
    • 1970-01-01
    • 2019-11-02
    • 2012-06-13
    • 1970-01-01
    • 1970-01-01
    • 2019-09-09
    • 1970-01-01
    相关资源
    最近更新 更多