9

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

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

4

3 回答 3

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使用。

于 2012-02-12T20:02:50.687 回答
3

一些环境或多或少直接支持这一点。

例如,如果您通过编译器开关启用结构化异常处理和 C++ 异常/EH,则可以通过 Microsoft 的结构化异常处理(C 的“异常”)实现 C++ 异常。如果在编译所有代码(两端的 C++ 和中间的 C)时设置了这些选项,堆栈展开将“起作用”。

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

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

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

你看,事情是大多数 C 代码不是为了在函数执行的任何时候处理 C++ 风格的堆栈展开而编写的。此外,它缺少析构函数,因此它没有RAII来保护自己免受异常影响。

Kenny TM为基于 C++11 和 Boost 的项目提供了解决方案。 xxbbcc对于一般情况有一个更通用但更繁琐的解决方案。

于 2012-02-12T20:14:46.203 回答
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 函数,该 C 函数反过来会在内部捕获异常,如果它们发生,则会在其自身方面引发异常。

于 2012-02-12T20:03:29.517 回答