【问题标题】:Catch exception when class definition is not visible当类定义不可见时捕获异常
【发布时间】:2018-11-02 14:41:02
【问题描述】:

我正在处理一个损坏的输入文件导致引发异常的问题。异常类在实现文件中定义,因此对我不可见。它确实继承自 std::exception

我尝试简单地向前声明异常类,因为我只是通过引用来捕获它。但是,这给了我一个 error: invalid use of incomplete type 编译器错误(在 Linux 上使用 GCC 6.2)。我想编译器需要完整的异常对象类型,以便它可以在需要时重新抛出异常。

所以这就是我想做的:

// library.cpp

namespace FOO {

struct SomeException : public std::exception
{
    // string member, virtual dtor, ctor taking one arg and virtual what()
};

void doStuff() {
}

}


// my main.cpp
namespace FOO
{
   struct SomeException;
}

int main()
{
    try
    {
        FOO::doStuff();
    }
    catch (FOO::SomeException& e)
    {
        // ignore e, but I know where it came from so log
        // an appropriate message
    }
    catch (std::exception& e)
    {
        // most other exceptions, log `what()` message
    }
    catch(...)
    {
        // tell user to contact customer support
    }
}

仅打印 what() 消息不适合我的上下文。

我可以要求其他团队将他们的异常类定义移动到标题中。这可能是一个缓慢的过程。我想我也可以对what() 消息进行字符串比较,但这看起来很难看。

还有其他选择吗?

(顺便说一句,我无法通过 Google 看到任何提及,但这似乎是一种反模式,即“仅抛出异常”)。

【问题讨论】:

  • 我会说这是实现该异常的人的设计错误。似乎正确的做法是要求他们修复它。
  • 您正在处理一个损坏/错误的库,应该要求修复它。传播到用户代码的任何异常的定义必须对用户代码可见。前向声明它没有帮助,因为你不能使用前向声明访问一个对象(我认为你需要访问这个对象,否则,你为什么要抓住它)?
  • @Francois 这就是我害怕人们会说的话。
  • 如果可以说服他们改变它,他们可能应该这样做。如果不是......是的,通过引用捕获是正确的方法 - 但你可能会通过指针捕获,在这种情况下你不应该需要类定义,我认为前向定义应该足够了。您将遇到谁拥有异常的问题,因此应该删除它 - 如果是您,对象切片的问题,并且他们还没有实现虚拟析构函数。考虑到所有因素,最好让他们改变它。
  • @SergeyA 我现在不需要访问异常对象。我只是希望能够捕获它以便能够记录比“出现问题”更具体的消息。

标签: c++ exception


【解决方案1】:

如果您无权访问原始类,您将无法正确捕获它:

C++ 标准/[except.handle]:

handler 中的 exception-declaration 描述了 可能导致输入该处理程序的异常。 exception-declaration 不应表示不完整的类型、抽象类类型或右值引用类型。 exception-declaration 不应表示指向不完整类型的指针或引用,除了 [cv void*]。

因此没有理想且干净的解决方案。但也许是一种可接受的解决方法:从std::exception 派生的类是多态的。因此,您可以考虑使用typeid()(最终与type_index 结合使用)来识别catch (std::exception& e) 块中的真实类型。

恕我直言,当.what() 不是替代方案时,它应该是区分未知异常的可接受方式。然而,不便之处在于type_info 数据的值(例如typeid(e).name())在标准中没有定义,这使得任何硬编码的值都不可移植。

概念证明:

//somewhere
class MyExcept : public std::exception { };

...

// somewhere else
try {
    throw std::exception();
} catch (std::exception &e) {
    std::cout <<"case 1: " << typeid(e).name() << std::endl;
}

try {
    throw MyExcept();
} catch (std::exception &e) {
    std::cout <<"case 2: "<< typeid(e).name() << std::endl;
}

Online demo

【讨论】:

  • 感谢您确认我的怀疑。在更改库之前,这似乎是我能做的最好的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
  • 2010-10-08
  • 2017-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多