Каков будет правильный способ создания API C ++ для старого кода C, который широко использует longjmp с несколькими целями перехода для управления ошибками?
Моя идея заключалась в том, чтобы написать функцию, которая устанавливает цели перехода для каждой используемой цели, например:
void catchJumps() {
if (setjmp(target1)) throw Error1(); //Error1 and Error2 are some exception classes
if (setjmp(target2)) throw Error2();
//...
}
Тогда я бы позвонил catchJumps
в каждой C ++ — функции (в каждой области, если быть более точным), использующей код C:
int some_wrapper() {
catchJumps();
callCFunction()
for (int i = 0; i < 1000; i++) {
catchJumps();
callOtherCFunction();
}
catchJumps();
callOneMoreCFunction();
callEvenOneMoreCFunction();
}
Это безопасный способ поймать все longjumps, не разрушая стек? Я знаю, что longjmp опасно вставлять в другой фрейм стека. Теперь моя функция catchJumps
находится в другом кадре стека, чем вызывающий some_wrapper
, Я надеюсь (или я могу даже сделать это) catchJumps может быть встроен, так что кадр такой же, но я не знаю.
Вызов в каждой области видимости (и после цикла выше) должен быть необходим для вызова всех деструкторов объектов с областью видимости, верно?
Если это недопустимый метод для «преобразования» longjmps в утверждения для вызывающего приложения, что еще мы можем сделать?
У вас могут возникнуть проблемы при использовании catchJumps
с автоматическими объектами, которые имеют деструкторы, как описано в https://stackoverflow.com/a/1376099/471164 и цитируя 18.7 / 4 «Другая поддержка времени выполнения»:
Если какие-либо автоматические объекты будут уничтожены брошенным исключением
передача управления другому (пункту назначения) в программе,
затем вызов longjmp (jbuf, val) в точке выброса, которая передает
управление той же (целевой) точкой имеет неопределенное поведение.
Я думаю, что лучший подход заключается в создании оболочки для каждой функции C, которую вы используете и которая может сделать longjmp
переводя все эти нелокальные goto в исключения. Это также сделает ваш код чище, потому что у вас не будет catchJumps()
повсюду, но только в этих функциях-оболочках.
Поскольку вы застряли с таким API в библиотеке, как насчет того, чтобы иметь catchJumps
выполнить фактический вызов, требуя передачи вызываемого объекта с нулевым параметром и используя указатель функции или boost/std::function
?
template <typename CallMe>
void catchJumps(CallMe wrappee)
{
if (setjmp(target1)) throw Error1(); //Error1 and Error2 are some exception classes
if (setjmp(target2)) throw Error2();
//...
wrappee();
}
int some_wrapper()
{
catchJumps(&callCFunction);
for (int i = 0; i < 1000; i++)
{
catchJumps(&callOtherCFunction);
}
catchJumps(&callOneMoreCFunction);
catchJumps(&callEvenOneMoreCFunction);
}