Я работаю над приложением C ++, которое использует библиотеку, написанную на C другой командой. Авторы библиотеки любят звонить exit()
когда происходят ошибки, это немедленно завершает программу, не вызывая деструкторы объектов в стеке в приложении C ++. Приложение настраивает некоторые системные ресурсы, которые не возвращаются операционной системой автоматически после завершения процесса (области общей памяти, мьютексы между процессами и т. Д.), Так что это проблема.
У меня есть полный исходный код как для приложения, так и для библиотеки, но библиотека очень хорошо себя зарекомендовала и не имеет модульных тестов, поэтому ее изменение будет иметь большое значение. Есть ли способ «перехватить» звонки на exit()
чтобы я мог осуществить корректное завершение работы моего приложения?
Одна возможность, которую я рассматриваю, — создать один большой класс, который является приложение — то есть вся очистка будет происходить либо в его деструкторе, либо в деструкторе одного из его членов — затем выделяет один из этих больших объектов в куче в main()
, установив глобальный указатель, чтобы указать на него, и используя atexit()
зарегистрировать обработчик, который просто удаляет объект через глобальный указатель. Это может сработать?
Есть ли известный хороший способ решения этой проблемы?
В самом худшем случае вы всегда можете написать свою собственную реализацию exit
и связать это, а не собственную реализацию системы. Вы можете обработать ошибки там, и при необходимости позвонить _exit(2)
сам.
Поскольку у вас есть источник библиотеки, это еще проще — просто добавьте -Dexit=myExit
флаг при его создании, а затем обеспечить реализацию myExit
,
установить обработчик выхода с atexit и реализовать желаемое поведение
Если вы хотите сделать библиотеку C более удобной в использовании из C ++, вы можете запустить ее в отдельном процессе. Затем убедитесь (с помощью обработчика выхода или иным образом), что при его выходе ваш основной процесс приложения замечает и выдает исключение для размотки своего собственного стека. Возможно, в некоторых случаях он может обработать ошибку нефатальным способом.
Конечно, перенести использование библиотеки в другой процесс может быть не просто и не особенно эффективно. У вас будет некоторая работа, чтобы обернуть интерфейс и скопировать входы и выходы с помощью механизма IPC по вашему выбору.
Как обходной путь для использования библиотеки из вашего основного процесса, я думаю, что тот, который вы описали, должен работать. Риск состоит в том, что вы не можете идентифицировать и изолировать все, что нужно очистить, или что кто-то в будущем изменит ваше приложение (или другой компонент, который вы используете), исходя из предположения, что стек будет нормально разматываться.
Вы мог изменить исходный код библиотеки для вызова настраиваемой во время выполнения или во время компиляции функции вместо вызова exit()
, Затем скомпилируйте библиотеку с обработкой исключений и реализуйте функцию в C ++ для генерации исключения. Проблема в том, что сама библиотека, вероятно, теряет ресурсы при ошибке, поэтому вам придется использовать это исключение только размотать стек (и, возможно, сделать некоторые сообщения об ошибках). Не поймите это и продолжайте, даже если ошибка может быть не смертельной, насколько это касается вашего приложения.
Если звонок exit
и не assert
или же abort
Есть несколько моментов, чтобы снова получить контроль:
exit
, деструкторы для объектов со статическим временем жизни (по существу: глобалы и объекты, объявленные с static
) все еще исполняются. Это означает, что вы можете установить (несколько) глобальных объектов «менеджер ресурсов» и освободить ресурсы в их деструкторе (ах).atexit
, Это не ограничено одним. Вы можете зарегистрироваться больше.Если все остальное терпит неудачу, потому что у вас есть источник библиотеки, вы можете сыграть несколько макросов, чтобы эффективно заменить вызовы exit
с вашей собственной функцией, которая может, например, вызвать исключение.