Решение setjmp
, предложенное @Shawn, должно работать, если оно не переполняет стек setjmp (и помните, что для рекурсивных синтаксических анализаторов может потребоваться достаточно большой стек), но оно накладывает довольно значительные издержки на каждый вызов для небольшой оптимизации возвратов, которые пропускают несколько кадров стека.
В модели рекурсивного всплытия количество пропущенных кадров мало, часто равно 0. Таким образом, накладные расходы будут большими, а экономия - небольшой.
Вы могли бы написать несколько более быстрое решение, используя libunwind (см. unw_step()
и unw_resume()
), но обратите внимание, что unw_step
обрабатывает стек вызовов как связанный список (что он и делает is), и, следовательно, может перешагивать только один кадр стека за раз. Таким образом, вы в конечном итоге с oop вокруг unw_step
звонков. Кроме того, вы должны убедиться, что вызов функции не был встроен.
Более простое и быстрое решение - просто обернуть CALL
и RETURN
в макросы, как подсказывает @shawn, и использовать возвращаемое в противном случае неиспользуемое возвращаемое значение для подсчета раскручивания: (Немного изменено для использования variadi c макросы)
#include <stdio.h>
int sp = 0;
#define CALL(f, ...) \
do { \
++sp; \
RETLEVEL(f(__VA_ARGS__)); \
} while (0)
#define RETLEVEL(n) \
for ( int n__ = n; n__ > 0 && sp > 0; ) { \
--sp; \
return n__ - 1; \
}
#define RETURN RETLEVEL(1)
int f3(void) {
printf("In f3(), sp is %d, returning back 2 levels\n", sp);
RETLEVEL(2);
}
int f2(void) {
printf("In f2(), calling f3(), sp is %d\n", sp);
CALL(f3);
printf("Returning from f2(), sp is %d\n", sp);
RETURN;
}
int f1(void) {
printf("In f1(), calling f2(), sp is %d\n", sp);
CALL(f2);
printf("Returning from f1(), sp is %d\n", sp);
RETURN;
}
int main(void) {
printf("In main(), calling f1(), sp is %d\n", sp);
CALL(f1);
printf("Returning from main(), sp is now %d\n", sp);
return 0;
}