Подобие объекта функции на языке C
Я попытался создать подобие объекта функции, которые, например, мы можем увидеть в JS. Код основан на макросах, дабы всё выглядело красивее
typedef struct functionCall{
jmp_buf out;
} functionCall;
functionCall *staticFunctionCall;
typedef struct Function{
jmp_buf in;
} Function;
#define function(func) \
malloc(sizeof(Function)); \
if (setjmp(func -> in)){ \
functionCall *FUNCTION_CALL = staticFunctionCall;
#define endInit \
longjmp(FUNCTION_CALL -> out,1); \
}
#define call(func) \
staticFunctionCall = malloc(sizeof(functionCall)); \
if (!setjmp(staticFunctionCall -> out)){ \
longjmp(func -> in, 1); \
}
void run(Function *f){
printf("Alert0\n");
if (1) {
call(f)
}
printf("Alert2\n");
}
int main(){
int g=1;
Function *f = function(f){
printf("Alert1\n");
g=0;
} endInit
run(f);
printf("Alert3\n");
}
Так вот, сразу не пугайтесь моего ужасного кода, ниже я объясню, как все по моей теории работает. function(func) - это макрос, который создает метку в коде, ссылка на которую находится как раз в структуре Function, которая и принимает вид желанного результата. Попробуем "вызвать" нашу функцию. Для этого я использую макрос call, который как раз возвращается к той метке. Выполнение возвращается к этому фрагменту кода, однако мне не удается вернутся обратно, откуда как раз и был произведен вызов. По итогу, код ломается, а вывод в консоль оканчивается строкой "Alert1". Я прекрасно понимаю, что подобные проблемы могут возникнуть и в случае, когда инициализация происходила в уже завершенном методе. Однако, не стоит пока в это углубляться. Мне лишь важно знать, как тогда правильно реализовать данную затею
Ответы (1 шт):
Код в вопросе выглядит конечно интересно, но возможно я не до конца понял пользу и цель сохранения контекста (setjmp) и последующего перехода (longjmp). Простейший аналог который я могу предложить выглядит так:
void run(void * f){
printf("1\n");
void (*func) (void) = f;
func();
printf("3\n");
}
int main(int argc, char const *argv[]){
void f(){
printf("2\n");
}
printf("0\n");
run(f);
printf("4\n");
return 0;
}
Что касается хороших примеров использования вышеуказанных инструкций:
#include <stdio.h>
#include <setjmp.h>
#include <stdnoreturn.h>
jmp_buf jump_buffer;
noreturn void a(int count)
{
printf("a(%d) called\n", count);
longjmp(jump_buffer, count+1); // вернет count + 1 из setjmp
}
int main(void)
{
volatile int count = 0;
if (setjmp(jump_buffer) != 9)
a(++count);
}
/* напечатает:
a(1) called
a(2) called
a(3) called
a(4) called
a(5) called
a(6) called
a(7) called
a(8) called
*/
Следующая программа выводит «1 2 3»:
#include <stdio.h>
#include <setjmp.h>
jmp_buf ebuf;
void f2 (void);
int main(void){
char first = 1;
int i;
printf("1 ");
i = setjmp(ebuf); // <+
if (first) { // |
first =! first; // |
f2(); // _________+
printf ("не будет напечатано");
}
printf("%d", i);
return 0;
}
void f2(void){
printf("2 ");
longjmp(ebuf, 3);
}
Собственно говоря эти примеры хорошо показывают куда происходит прыжок, а именно: туда где вызвана инструкция setjmp. По этой причине в коде вопроса и происходит зацикливание.
Решение задачи:
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <stdnoreturn.h>
typedef struct functionCall{
jmp_buf out;
} functionCall;
typedef struct Function{
jmp_buf in;
} Function;
functionCall *staticFunctionCall;
#define function(func) \
malloc(sizeof(Function)); \
functionCall *FUNCTION_CALL = staticFunctionCall; \
if (setjmp(func -> in)){ \
printf("func start\n");
#define endInit(breaker) \
printf("func end\n"); \
longjmp(breaker ,1); \
}
#define call(func) \
staticFunctionCall = malloc(sizeof(functionCall)); \
if (setjmp(staticFunctionCall -> out) != 1){ \
printf("start call\n"); \
longjmp(func -> in, 2); \
printf("end call\n"); \
}
jmp_buf breaker;
void run(Function *f){
printf("run start\n");
call(f)
printf("run end\n");
}
int main(){
int r;
r = setjmp(breaker);
if (!r){
Function *f = function(f){
printf("func body\n");
} endInit(breaker)
run(f);
}
printf("end\n");
}
результат:
run start
start call
func start
func body
func end
end