代码之家  ›  专栏  ›  技术社区  ›  sharptooth

C++异常会安全地通过C代码传播吗?

  •  30
  • sharptooth  · 技术社区  · 15 年前

    我有一个C++应用程序调用 SQLite 's(sqlite为c) sqlite3_exec() 这又可以调用我在C++中实现的回调函数。sqlite被编译成一个静态库。

    如果一个异常逃脱了我的回调,它会通过SQLite的C代码安全地传播到C++代码调用SQLITE3XEXECUTE()吗?

    5 回复  |  直到 15 年前
        1
  •  29
  •   anon    15 年前

    我的猜测是这取决于编译器。但是,在回调中抛出异常是一个非常糟糕的主意。要么它将完全失效,要么SQLite库中的C代码将无法处理它。如果这是sqlite中的一些代码,请考虑:

    {
      char * p = malloc( 1000 );
      ...
      call_the_callback();  // might throw an exception
      ...
      free( p );
    }
    

    如果异常“有效”,C代码就无法捕获它,P将永远不会被释放。当然,图书馆可能分配的其他资源也是如此。

        2
  •  14
  •   Hans Passant    15 年前

    已经有回调中止API调用的协议。从 the docs 以下内容:

    如果sqlite3_exec()回调返回 非零,sqlite3_exec()例程 返回sqlite_abort而不调用 重新调用而不运行 任何后续的SQL语句。

    我强烈建议您使用这个而不是例外。

        3
  •  10
  •   AnthonyLambert    15 年前

    sqlite希望您返回sqlite_abort on error和0返回代码no error。所以你应该 在尝试捕获中包装所有C++回调 . 然后在catch中返回sqlite sqlite_abort错误代码,否则返回零。

    如果绕过通过sqlite返回,则会出现问题,因为从回调返回后,它不会释放/完成任何代码。这将导致无法解决的问题,其中一些可能非常模糊。

        4
  •  1
  •   Gianni    15 年前

    这是一个非常有趣的问题,我出于好奇对它进行了测试。在我的操作系统x w/gcc 4.2.1中,答案是“是”。它工作得很好。我认为一个真正的测试是使用GCC为C++和其他(MSVC)。LLVM?)对于C部分,看它是否仍然有效。

    我的代码:

    愈伤组织

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    typedef void (*t_callb)();
    void cfun(t_callb fn);
    
    #ifdef __cplusplus
    }
    #endif
    

    胼胝体:

    #include "callb.h"
    
    void cfun(t_callb fn) {
     fn();
    }
    

    主.cpp:

    #include <iostream>
    #include <string>
    #include "callb.h"
    
    void myfn() {
      std::string s( "My Callb Except" );
      throw s;
    }
    
    int main() {
      try {
        cfun(myfn); 
      }
      catch(std::string s) {
        std::cout << "Caught: " << s << std::endl;
      }
      return 0;
    }
    
        5
  •  0
  •   Francis Boivin    15 年前

    如果从sqlite调用的回调来自调用sqlite3_exec()的同一线程,则调用堆栈中的某个位置的throw应该由更高级别的catch捕获。

    亲自测试应该很简单,不是吗?

    [编辑] 在深入挖掘自己之后,我发现C++标准对于抛出异常时C调用的C++函数的行为有些模糊。

    您一定要使用API期望的错误处理机制。否则,您将主要是API本身处于未定义状态,任何进一步的调用都可能失败/崩溃。