Передача исключений через границу C API - PullRequest
9 голосов
/ 12 февраля 2012

Я пишу библиотеку на C ++, которая использует более старый C API.Клиент моей библиотеки может указывать функции обратного вызова, которые косвенно вызываются через мою библиотеку, которая вызывается через C API.Это означает, что все исключения в клиентских обратных вызовах должны быть обработаны.

Мой вопрос таков: как я могу перехватить исключение на одной стороне границы и перебросить его после пересечения границы C API ивыполнение вернулось в землю C ++, так что исключение может быть обработано клиентским кодом?

Ответы [ 3 ]

5 голосов
/ 13 февраля 2012

С 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 .

3 голосов
/ 13 февраля 2012

Некоторые среды поддерживают это более или менее напрямую.

Например, если вы включите структурированную обработку исключений и C ++ с помощью переключателя компилятора /EH,Вы можете реализовать исключения C ++ поверх структурированной обработки исключений Microsoft («исключения» для C).При условии, что эти опции установлены при компиляции всего вашего кода (C ++ на каждом конце и C в середине), раскручивание стека будет «работать».

Однако, это почти всегда плохая идея (TM)). Почему, спросите вы?Учтите, что фрагмент кода C в середине:

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

И что invoke_cxx_callback() (.... барабанная дробь ...) вызывает ваш код C ++, который выдает исключение.Вы будете пропускать блокировку мьютекса.Ой.

Видите ли, дело в том, что большая часть кода C не написана для обработки раскручивания стека в стиле C ++ в любой момент при выполнении функции.Более того, в нем отсутствуют деструкторы, поэтому у него нет RAII для защиты от исключений.

Kenny TM имеет решение для C ++ 11 и Boost-основанные проекты. xxbbcc имеет более общее, хотя и более утомительное решение для общего случая.

0 голосов
/ 13 февраля 2012

Вы, вероятно, можете передать структуру через интерфейс 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, которая, в свою очередь, будет перехватывать исключения внутри себя иесли они случаются, выдает исключение на своей стороне.

...