объект не уничтожается после перехвата исключения в программировании смешанного кода C и C ++ - PullRequest
0 голосов
/ 06 июля 2018

Я пишу программу, использующую смешение c и c ++, и сталкиваюсь с проблемой уничтожения объектов в обработчике исключений c ++.Я написал простой случай для воспроизведения проблемы.

main.cpp

#include <iostream>

extern "C" void test(void(*f)(void));

struct foo {
    ~foo() {
        std::cout << "foo destruction" << std::endl;
    }
};

void error_handler(void) {
    throw 1;
}

int main() {
    try {
        foo f;
        test(error_handler);
    } catch (...) {

    }
}

test.c

void test(void(*handler)(void)) {
    handler();
}

Когда я создавал это в Visual Studio 2015 и Visual Studio 2017, деструктор foo не вызывался.Но когда я тестирую его с помощью gcc 5.4, деструктор foo работает нормально.

Можно ли вызвать исключение C ++ в коде C посредством вызова указателя на функцию (указатель на функцию, реализованную в коде cpp)?Является ли приведенный выше код незаконным или это просто ошибка msvc?

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Механизм исключений Windows явно предназначен для запуска деструкторов и, наконец, очистки типов даже при наличии исключений и аналогичных из других языков.

То, что вы спрашиваете, должно хорошо работать на Windows - это то, для чего оно предназначено.

Однако вам нужно явно включить это в Visual Studio. По умолчанию Visual Studio устанавливает код C ++ с моделью исключений /EHsc, которая явно предполагает, что extern "C" функции не генерируют и не пропускают исключения . Это оптимизация, и, как правило, хорошая.

Однако, если вам нужно предположить, что функции extern "C" генерируют или пропускают исключения, вам нужно изменить модель исключения. Вы, вероятно, хотите /EHs.

Тем не менее, я бы рекомендовал ознакомиться с последствиями этого, прежде чем изменить его здесь .

Edit: использовать эту функцию или нет, является дискуссионным. Как правило, с исключениями (и другими подобными механизмами) весь код в стеке между метателем и вызывающей стороной должен быть безопасным для исключения. Если вы владеете кодом, то это нормально, если в стеке есть такие вещи, как обратные вызовы Windows или другие библиотеки, вам нужно найти гарантию, что это нормально. И вообще для Windows внутренний код его нет.

0 голосов
/ 06 июля 2018

Бросок исключений через языковые границы всегда оставляет программу в несовместимом состоянии. Разматывание стека, выполняемое в случае возникновения исключения C ++, гарантированно будет работать только с (двоично-совместимыми) фреймами стека C ++. Язык Си даже не имеет понятия об исключениях. Даже если при разматывании стека удается размотать кадры стека C, он не будет выполнять их очистку. Таким образом, любая функция обратного вызова, передаваемая в C-код, должна быть объявлена ​​как noexcept и обрабатывать ошибки некоторым способом, который не включает в себя выброс исключения через границу языка:

void error_handler(void) noexcept {
   try
   {
      throw 1;
   } 
   catch(…)
   {
       // TODO convert to error code or store for later using exception_ptr
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...