История изменений
Исправление vbr, (текущая версия) :
А как же сишное скрепное «никакого неявно выполняемого кода»?
Согласен, это нарушение этого принципа. С другой стороны функционал явно полезный и как сохранить пользу, не нарушая данный принцип - вопрос интересный…
Попробую на примере сформулировать проблему и подумать.
Как сейчас пишут:
int f(int x) {
char *s = malloc(1);
if (x == 0) {
free(s);
return 0;
}
free(s);
return 1;
}
Как предлагается:
void f(int x) {
char *s = malloc(1);
defer free(s);
if (x == 0) {
// free(s);
return 0;
}
// free(s);
return 1;
}
Видим, что перед return неявно вставляется free(s).
Как этот код пишут «опытные» сишники:
int f(int x) {
int result;
char *s = malloc(1);
if (x == 0) {
result = 0;
goto end;
}
result = 1;
end:
free(s);
return result;
}
У обоих подходов без defer есть очевидная проблема - никто не заставляет писать goto end. Можно написать return 0
и ничего не освободится.
Т.е. можно сформулировать проблему - какой синтаксис придумать, чтобы с одной стороны не было неявного выполнения кода, с другой стороны компилятор заставлял бы освобождать ресурс.
Можно, к примеру, такой подход использовать:
int f(int x) {
int result;
char *s = malloc(1);
must_visit free_s;
if (x == 0) {
result = 0;
goto free_s;
}
result = 1;
free_s:
free(s);
return result;
}
По сути этот код идентичен предыдущему. Всё на 100% явно, никакого неявного кода или неявных переходов. Но тут есть конструкция must_visit <label>
. По задумке эта конструкция заставляет компилятор верифицировать, что все пути выполнения программы после этой конструкции достигают указанной метки. Если написать return 0
, то компилятор увидит, что в этом случае метка free_s
будет недостижима и выдаст ошибку компиляции.
Возможно этот подход был бы более практичным с точки зрения адаптации существующего кода. Хотя, безусловно, defer выглядит на порядок «симпатичней». Наверное иногда принципами лучше поступаться.
Исходная версия vbr, :
А как же сишное скрепное «никакого неявно выполняемого кода»?
Согласен, это нарушение этого принципа. С другой стороны функционал явно полезный и как сохранить пользу, не нарушая данный принцип - вопрос интересный…
Попробую на примере сформулировать проблему и подумать.
Как сейчас пишут:
int f(int x) {
char *s = malloc(1);
if (x == 0) {
free(s);
return 0;
}
free(s);
return 1;
}
Как предлагается:
void f(int x) {
char *s = malloc(1);
defer free(s);
if (x == 0) {
// free(s);
return 0;
}
// free(s);
return 1;
}
Видим, что перед return неявно вставляется free(s).
Как этот код пишут «опытные» сишники:
int f(int x) {
int result;
char *s = malloc(1);
if (x == 0) {
result = 0;
goto end;
}
result = 1;
end:
free(s);
return result;
}
У обоих подходов без defer есть очевидная проблема - никто не заставляет писать goto end. Можно написать return 0
и ничего не освободится.
Т.е. можно сформулировать проблему - какой синтаксис придумать, чтобы с одной стороны не было неявного выполнения кода, с другой стороны компилятор заставлял бы освобождать ресурс.
Можно, к примеру, такой подход использовать:
int f(int x) {
int result;
char *s = malloc(1);
must_visit free_s;
if (x == 0) {
result = 0;
goto free_s;
}
result = 1;
free_s:
free(s);
return result;
}
По сути этот код идентичен предыдущему. Всё на 100% явно, никакого неявного кода или неявных переходов. Но тут есть конструкция must_visit <label>
. По задумке эта конструкция заставляет компилятор верифицировать, что все пути выполнения программы после этой конструкции достигают указанной метки. Если написать return 0
, то компилятор увидит, что в этом случае метка free_s
будет недостижима и выдаст ошибку компиляции.
Возможно этот подход был бы более практичным с точки зрения адаптации существующего кода.