C ++自定义异常:运行时性能并将异常从C ++传递到C(作为错误信息)

时间:2010-04-21 09:25:47

标签: c++ c exception-handling

我正在编写一个自定义C ++异常类(因此我可以通过C API将C ++中出现的异常传递给另一种语言)。

我最初的攻击计划是按照以下步骤进行:

//C++ 
myClass
{
public:
   myClass();
   ~myClass();

   void foo() // throws myException
   int foo(const int i, const bool b) // throws myException
} * myClassPtr;


// C API
#ifdef __cplusplus
extern "C" {
#endif

myClassPtr MyClass_New();
void MyClass_Destroy(myClassPtr p);
void MyClass_Foo(myClassPtr p);
int  MyClass_FooBar(myClassPtr p, int i, bool b);

#ifdef __cplusplus
};
#endif

我需要一种能够将C ++代码中抛出的异常传递给C端的方法。我想传递给C方的信息如下:

(a)中。什么 (b)中。哪里 (C)。简单堆栈跟踪(只是它们发生的错误消息序列,没有调试信息等)

我想修改我的C API,以便API函数获取指向结构ExceptionInfo的指针,该结构将在使用调用结果之前包含任何异常信息(如果发生异常)。

因此,在C API中公开的C ++方法将实现如下:

//我必须承认,对于这种实施(维护噩梦除外)有一些奇怪的“ha”“ - 这一定是更好的方法吗?

// ExceptionInfo default ctor initialized to no error

void MyClass::foobarTrappedExceptionWrapper(ExceptionInfo& ei)
{    
    try {
      // call foobar ...
      foobar();
    }
    catch(fast_exception& fe)
    {
       switch (fe.errcode())
       {
          //package exception info into ei for C consumption 
       }
    }
    catch(std::exception &e)
    {    
      //package exception info into ei for C consumption 
    }
}

C API中公开的每个C ++方法的实现都需要包含在try / catch语句中(参见上面的代码片段)。对此的性能影响似乎非常严重(根据此article):

  

“这是一个错误(具有高运行时间   成本)使用C ++异常处理   对于频繁发生的事件,或   对于在附近处理的事件   检测点。“

与此同时,我记得在C ++时代的某个地方读过,虽然异常处理都很昂贵,但实际发生异常时只会造成代价高昂。那么,哪个是正确的?该怎么办?。是否有另一种方法可以安全地捕获错误并将结果错误信息传递给C API?或者这是一个次要考虑因素(毕竟这篇文章很老了,从那以后硬件有所改进)。

[编辑]

删除了问题2,因为我找到了一种创建简单堆栈跟踪here的方法。

1 个答案:

答案 0 :(得分:2)

的答案

     
  1. 两者都是正确的;你误解了第一个来源。 try {}块基本上没有成本;它是抛出......捕获(即传播)的昂贵。因此,如果没有发生异常,这是很便宜的,这就是为什么它们不应该被使用,如果它们经常出现(因为那时你经常抛出......)。
  2.  
  3. 使用非常长但固定大小的缓冲区并不比使用std :: string更轻。只有当你担心由于内存不足而引发异常时才使用std :: string是一个坏主意(在这种情况下std :: string构造函数会因为无法分配空间而抛出)。但是,为什么重新发明轮子?我建议你看看boost::exception,它提供了一个boost :: exception类,它允许将任意元数据附加到异常对象上,并且它可以是轻量级(没有附加元数据)或者重量很重你想要的,取决于你使用的元数据。
  4. 评论在C中传递C ++异常
    继承或不继承std :: exception的选择对性能没有影响;文章提到传播异常和执行堆栈展开的事实是昂贵的(无论异常的类型)。在C世界中传递异常方面......错误处理C通常使用整数返回类型和错误代码完成,但是如果你想传递一个C ++对象,那么如果你可以多态复制异常类型,然后你可以简单地在堆上构造一个副本,然后传递异常对象作为类型 void * ,如果你需要从该对象中提取信息,那么你可以创建C在C ++中实现的接口,它将 void * 对象强制转换为指向异常类型的指针并提取相应的字段。另一种选择,如果您想要的是what()消息,则传递消息的strdup()。但请记住,如果std :: bad_alloc是您处理的异常之一,那么需要分配/复制的任何东西都不会起作用(换句话说,如果由于内存不足而导致失败,则分配更多内存不是个好主意。)

    俗话说“在罗马时,像罗马人那样做”......我自己的建议是在C语言中使用返回状态代码和错误代码,并在C ++中使用异常处理。我真的不愿意尝试将C ++异常对象引入C世界。还要记住,C ++代码调用C代码比调用C ++代码的C代码更常见。如果要为C ++代码创建C交互,那么它们应该像C接口一样,即返回错误状态代码。如果您的C ++代码调用返回错误状态代码的C函数,则抛出异常而不是返回基础C代码失败的错误状态代码是合理的。但是,在这种情况下,您处于C ++空间并且不必担心异常交叉回C代码。但是,如果您回到C世界,我建议您记录您拥有的任何信息并使用错误状态代码。

    另外,假设std :: exception :: what()返回一个描述发生了什么的char *对象,而你的API返回一个表示异常类型的整数代码......好吧,这对其他人来说真的很混乱使用你的代码。当我看到你在switch语句中使用waht()时,我就要评论你不能打开一个字符串,然后不得不进行双重操作并意识到它意味着与平常不同的东西。