跨C API边界传递异常

时间:2012-02-12 19:53:27

标签: c++ exception exception-handling c-api

我正在用C ++编写一个使用旧版C API的库。我的库的客户端可以指定回调函数,这些函数通过我的库间接调用,该库通过C API调用。这意味着必须处理客户端回调中的所有异常。

我的问题是:如何在边界的一侧捕获异常,并在C API边界重新生成后重新抛出它,并且执行返回到C ++域中,以便客户端可以处理异常码?

3 个答案:

答案 0 :(得分:5)

使用C ++ 11,我们可以使用:

std::exception_ptr active_exception;

try
{
    // call code which may throw exceptions
}
catch (...)
{
    // an exception is thrown. save it for future re-throwing.
    active_exception = std::current_exception();
}

// call C code
...

// back to C++, re-throw the exception if needed.
if (active_exception)
    std::rethrow_exception(active_exception);

在C ++ 11之前,这些仍然可以通过Boost Exception使用。

答案 1 :(得分:3)

有些环境或多或少直接支持这种做法。

例如,如果通过structured exception handling编译器开关启用/EH和C ++异常,则可以通过Microsoft的结构化异常处理实现C ++异常("异常&#34 ;对于C)。如果在编译所有代码时设置了这些选项(每端都是C ++,中间是C),那么堆栈展开将会工作"工作"。

然而,这几乎总是一个坏主意(TM)。为什么,你问?考虑到中间的C代码是:

WaitForSingleObject(mutex, ...);
invoke_cxx_callback(...);
ReleaseMutex(mutex);

并且invoke_cxx_callback()(....鼓roll ...)调用引发异常的C ++代码。您将泄漏互斥锁。哎哟。

你知道,事实是大多数C代码都不是为了在函数执行中随时处理C ++风格的堆栈而展开的。此外,它缺乏析构函数,因此它没有RAII来保护自己免受异常。

Kenny TM有一个针对C ++ 11和基于Boost的项目的解决方案。对于一般情况,xxbbcc有一个更普遍但更繁琐的解决方案。

答案 2 :(得分:0)

您可以在C接口上传递一个结构,该结构在出现异常时会填充错误信息,然后在客户端收到该结构时,检查它并在客户端内部根据来自的数据抛出异常结构。如果只需要最少的信息来重新创建异常,则可以使用32位/ 64位整数作为错误代码。例如:

typedef int ErrorCode;

...

void CMyCaller::CallsClient ()
{
    CheckResult ( CFunction ( ... ) );
}

void CheckResult ( ErrorCode nResult )
{
    // If you have more information (for example in a structure) then you can
    // use that to decide what kind of exception to throw.)
    if ( nResult < 0 )
        throw ( nResult );
}

...

// Client component's C interface

ErrorCode CFunction ( ... )
{
    ErrorCode nResult = 0;

    try
    {
        ...
    }
    catch ( CSomeException oX )
    {
        nResult = -100;
    }
    catch ( ... )
    {
        nResult = -1;
    }

    return ( nResult );
}

如果您需要比单个int32 / int64更多的信息,那么您可以在调用之前分配一个结构并将其地址传递给C函数,反过来,它将在内部捕获异常,如果它们发生,则抛出异常自己的一面。