При взаимодействии C и C++ важно учитывать:
- Отсутствие исключений в C: Язык C не поддерживает исключения. Функции, написанные на C, не могут ни бросать, ни перехватывать исключения C++.
- Пересечение границ языка:
- Нельзя бросать исключение C++ из функции, помеченной как
extern "C". Это может привести к неопределенному поведению, так как механизм раскрутки стека C++ не сработает, а деструкторы локальных объектов могут не вызваться.
- При вызове функции C из C++-кода, которая может завершиться ошибкой, не следует полагаться на исключения. Необходимо обрабатывать ошибки, возвращаемые этой функцией, используя традиционные для C подходы (возвращаемые значения, глобальные переменные
errno).
- Обработка исключений в C++ коде, вызывающем C: Если C++ код вызывает функции C, которые могут завершиться ошибкой, эту ошибку нужно проверять после каждого вызова функции C. При обнаружении ошибки в C++ коде может быть брошено исключение.
c
- Использование
noexcept: Если C++-функция, вызываемая из C-кода, не должна бросать исключений, ее можно пометить как noexcept. Это четко указывает на отсутствие исключений и может помочь компилятору в оптимизации. Однако, следует помнить, что исключение, брошенное из такой функции, приведет к немедленному завершению программы (std::terminate).
- RAII (Resource Acquisition Is Initialization): Использование RAII в C++ для управления ресурсами (файлами, памятью, мьютексами) помогает гарантировать их освобождение даже при возникновении исключений в C++ коде. Объекты-обертки могут инкапсулировать вызовы функций C для управления ресурсами и обрабатывать ошибки, возвращаемые этими функциями.
c
- Функции обратного вызова (Callbacks): При передаче C++-лямбд или указателей на функции в C-код в качестве коллбэков, необходимо быть крайне осторожным с исключениями. Если функция C ожидает обычную C-функцию обратного вызова, бросание исключения из C++-коллбэка может привести к неопределенному поведению. В таких случаях необходимо перехватывать любые исключения внутри коллбэка перед возвращением управления C-коду.
Сводные правила:
| Сценарий | Рекомендация |
|---|
Бросить исключение из extern "C" | Никогда |
| Вызвать C из C++ | Проверять возвращаемые значения/errno, использовать RAII, бросать исключения в C++ при необходимости |
| Вызвать C++ из C (через коллбэк) | Перехватывать исключения в C++-коллбэке до возврата управления C-коду |
| C-функция, вызываемая из C++, не бросает | Пометить C++-функцию как noexcept (если это действительно так) |
Главное правило: механизм исключений C++ не должен пересекать границу extern "C" в сторону C. Ошибки из C должны обрабатываться традиционными для C способами и при необходимости транслироваться в исключения в коде C++.