Можно так ещё сделать:
— все ресурсы инициализируются неким сигнальным значением (NULL'ом, например)
— по ходу работы, выполняются захваты ресурсов, а их переменные переходят из сигнального значения в рабочее
— по завершении, будь то обычное или аварийное, освобождаются все ресурсы, содержащие рабочее значение
int foo()
{
int retval = 1; /* код ошибки */void* mem1 = 0;
void* mem2 = 0;
int handle = -1;
bool login_done = false;
/* поехали */
mem1 = malloc(1000);
if(!mem1) goto end; /* исключение, связанное с выделением ресурса */if(something_goes_wrong()) goto end; /* исключение по логике работы */
mem2 = malloc(1);
if(!mem2) goto end;
handle = open("x.txt", _O_READWRITE);
if(handle<0) goto end;
login(); login_done = true; /* пример чисто логического ресурса: пара login()-logout() */
retval = 0; /* код успеха */
end:
/* освобождаем в обратном порядке, либо в порядке, эквивалентном обратному */if(login_done) logout();
if(handle>=0) close(handle);
if(mem2) free(mem2);
if(mem1) free(mem1);
return retval;
}
Подход хорош тем, что у нас исчезают макароны из кучи меток или деревья из вложенных if-ов.
А плох, соответственно, некоторым оверхедом в конце программы.
Впрочем, если исключительные ситуации — именно исключительные, т.е. встречаются нечасто, то на провал производительности можно и подзабить.
Да и какой это провал? Тьфу, десяток if-ов над интегральными переменными.
Здравствуйте, sts, Вы писали:
sts>Мне видятся такие: sts>- необходимо иметь функцию установки в null, которая отрабатывает ДО конструктора, ну или совместить ее с конструктором; тогда тот должен четко разделять 2 фазы конструирования; sts>- необходимо иметь функцию проверки на null, которую нужно звать ДО деструктора, или доп.требование к деструктору — не удалять не инициализированные объекты.
Не совсем понятно, что вы подразумеваете по конструктором/деструктором в С.
Все начальные значения ресурсов устанавливаются перед инициализацией. Собсно вот то же самое, что я имею в виду: http://www.rsdn.ru/forum/cpp/4500007.1.aspx
Здравствуйте, sts, Вы писали:
sts>Всем привет.
sts>Вопрос как лучше обеспечить освобождение ресурсов в C ? sts>Ниже на вскидку 3 способа. sts>foo1 — просто копипастим освобождение ресурсов.
На пятой пасте начинаются ошибки слежения, на седьмой становится неуправляемым. В сад.
sts>foo2 — заменяем копипасту на goto на код освобождения.
Да, старый добрый надёжный метод. Конечно, кому-то не понравится goto.
Разновидность этого метода — что в отдельных переменных считается (счётчиком или булевыми флагами), что именно инициализировано, а код cleanup'а проверяет их.
sts>foo3 — заменяем похожие if-ы на макрос.
Да, работает. Но бывает, что компилятор не понимает, что код идентичен, и дублирует его в K копиях.
Если это не подходит, надо делать таки goto. Можно его замаскировать в макрос:)
Ещё один метод забыли: функция вызывается через переходник, который и освобождает ресурсы (а для удобства учёта этот переходник может, например, передать структурную запись с указателями). Тогда реальная функция ничего не освобождает сама и может иметь кучу точек выхода. Когда-то был любимый метод во всяких Clipper'ах, например, сохранить экран и восстановить его по выходу.;)
sts>Все способы, как кажется, — жесть какая-то :crash:
[...] sts>Т.е. понятно, что можно реализовать RAII на макросах, но почему-то не хочется :)
Вы не поверите — RAII тоже может выглядеть ужасно, если, например, захват и освобождение ресурса — это сложное действие типа перенастройки внешнего устройства, построение временного хранилища в виде каталога с файлами, и так далее. В этом случае под каждую такую задачу надо писать свой класс с деструктором для отката.
sts>int foo2() {
Вот этот вариант с кучей выходных меток, например, в линуксовом fork():
return p;
bad_fork_free_pid:
if (pid != &init_struct_pid)
free_pid(pid);
bad_fork_cleanup_io:
if (p->io_context)
exit_io_context(p);
bad_fork_cleanup_namespaces:
exit_task_namespaces(p);
bad_fork_cleanup_mm:
if (p->mm)
mmput(p->mm);
bad_fork_cleanup_signal:
[...]
Здравствуйте, hotdox, Вы писали:
H>Здравствуйте, sts, Вы писали:
sts>>Всем привет.
sts>>Вопрос как лучше обеспечить освобождение ресурсов в C ? sts>>Ниже на вскидку 3 способа. sts>>foo1 — просто копипастим освобождение ресурсов. sts>>foo2 — заменяем копипасту на goto на код освобождения. sts>>foo3 — заменяем похожие if-ы на макрос. sts>>ну и так далее: fooX — дальше плодим макросы (какие ?)
H>Мне придумалось как-то так: http://whalebot.blogspot.com/2010/10/raii-in-c.html
H>Вкратце: H>
Здравствуйте, abdab, Вы писали:
A>Здравствуйте, sts, Вы писали:
sts>>Мне видятся такие: sts>>- необходимо иметь функцию установки в null, которая отрабатывает ДО конструктора, ну или совместить ее с конструктором; тогда тот должен четко разделять 2 фазы конструирования; sts>>- необходимо иметь функцию проверки на null, которую нужно звать ДО деструктора, или доп.требование к деструктору — не удалять не инициализированные объекты.
A>Не совсем понятно, что вы подразумеваете по конструктором/деструктором в С. A>Все начальные значения ресурсов устанавливаются перед инициализацией. Собсно вот то же самое, что я имею в виду: http://www.rsdn.ru/forum/cpp/4500007.1.aspx
sts>может так получиться, что для 1-го получилось выделить, а для 2-го — нет. sts>Тогда удалять нужно только 1-й.
Тогда второй будет 0...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Писал из головы, мог накосячить. Отадку и переделку в том направении, чтобы уйти от аллокации на каждый push оставляю благодарному читателю...
Да, кстати, поверх этой хрени легко приделать и исклчения на чём-то воде лонгджампа...
Ну и вообще многие запары с владением, исключениями и прочей ерундой и в С++ разруливаются.
Скажем можно жить на каком-то отдельном аллокаторе, то, что надо таки рушить, регить в RaiiSupport'е, а в конце большого алгоритма таком или ином, звать RaiiRallback до 0 и рушить аллокатор целиком...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, sts, Вы писали:
К>Можно так ещё сделать: К>- все ресурсы инициализируются неким сигнальным значением (NULL'ом, например) К>- по ходу работы, выполняются захваты ресурсов, а их переменные переходят из сигнального значения в рабочее К>- по завершении, будь то обычное или аварийное, освобождаются все ресурсы, содержащие рабочее значение
все хорошо пока ресурс — это простой тип — число или указатель.
с предопределенными структурами уже хуже получается.
Здравствуйте, sts, Вы писали:
sts>Потому нужно определить сто такое для каждой 0 и как ее выставить в 0. sts>т.е. +2 функции к каждому типу.
В С типов нет... Есть структуры.
Скажем есть у теюя струтктура MyArray, и у неё есть поле body. Проверяешь его и всё...
Ну и вообще чё за страсть писать на С, как на С++? "pp" к расширению дописать ломает что ли?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, sts, Вы писали:
sts>>Потому нужно определить сто такое для каждой 0 и как ее выставить в 0. sts>>т.е. +2 функции к каждому типу.
E>В С типов нет... Есть структуры. E>Скажем есть у теюя струтктура MyArray, и у неё есть поле body. Проверяешь его и всё...
Уже есть определенные структуры с различным содержимым.
Не для всех можно проверить инициализированность.
E>Ну и вообще чё за страсть писать на С, как на С++? "pp" к расширению дописать ломает что ли?
Не так все просто, как хотелось бы. Приходится и на C тоже.
Писать по большому счету все равно на чем.
В разных языках одно и то же реализуется различными способами: RAII, GC, System.IDisposable, java.io.Closeable etc.
Я пытаюсь найти простой способ для С, т.к. иногда таки жесть получается
С системными вещами особых проблем нет, а вот как начинается боле-менее сложная логика — приходит кердык
Re[2]: [PURE C] Почему замена RAII, а не реализация???
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, sts, Вы писали:
sts>>Все способы, как кажется, — жесть какая-то
E>Обычно на С пишут немного не так, как на С++, так что получается вовсе и не жесть.
А как пишут на С ?
В этой ветке еще не все способы перечислили ?
По моим наблюдениям, в основном таки копипастят освобождение ресурсов перед каждым return.
Либо goto error_N.
Мне эти подходы не особо нравятся.
E>Но если вы жить не можете без RAII, ну заведите себе RAII!
Можно и так конечно, но хочется не RAII, а чего попроще, с Сишым подходом.
E>
E>Писал из головы, мог накосячить. Отадку и переделку в том направении, чтобы уйти от аллокации на каждый push оставляю благодарному читателю...
E>Да, кстати, поверх этой хрени легко приделать и исклчения на чём-то воде лонгджампа... E>Ну и вообще многие запары с владением, исключениями и прочей ерундой и в С++ разруливаются.
E>Скажем можно жить на каком-то отдельном аллокаторе, то, что надо таки рушить, регить в RaiiSupport'е, а в конце большого алгоритма таком или ином, звать RaiiRallback до 0 и рушить аллокатор целиком...
Здравствуйте, Кодт, Вы писали:
К>А плох, соответственно, некоторым оверхедом в конце программы.
Это ведь не просто овехед, это еще и перенос логики из времени компиляции во время исполнения. Кроме того (или может быть в частности) двухстадийная инициализация ресурса не дает возможности объявить его const.
Re[3]: [PURE C] Почему замена RAII, а не реализация???
Здравствуйте, sts, Вы писали:
sts>А как пишут на С ?
Функции немного другие пишут просто. И вообще программы.
sts>Мне эти подходы не особо нравятся.
А чем goto error_N плох?
E>>Но если вы жить не можете без RAII, ну заведите себе RAII!
sts>Можно и так конечно, но хочется не RAII, а чего попроще, с Сишым подходом.
AFAIK "Сшный подход" -- это goto error_N или if( resourceXXX != 0 ) freeResoureY( resourceXXX );
А то, что вы понаписали структур, по которым не понять инициализированы они или нет -- так кто же вам доктор-то?
Ксати, agregate initialization при таком подходе -- чудо, как хороша
типа, вместо
struct MySuperStruct xxx;
пишите
MySuperStruct xxx = { 0 };
А в то, что состояние структуры "все поля 0" может требовать что-то ещё освобоить я не верю
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, sts, Вы писали:
sts>>А как пишут на С ? E>Функции немного другие пишут просто. И вообще программы.
sts>>Мне эти подходы не особо нравятся. E>А чем goto error_N плох?
как минимум при изменениях кода едет нумерация меток.
да и витиевато выходит.
тут уже предложили ++stage с последующим switch (stage) для решения этой проблемы.
но, кажется, при этом становится еще витиеватее
E>>>Но если вы жить не можете без RAII, ну заведите себе RAII!
sts>>Можно и так конечно, но хочется не RAII, а чего попроще, с Сишым подходом.
E>AFAIK "Сшный подход" -- это goto error_N или if( resourceXXX != 0 ) freeResoureY( resourceXXX );
E>А то, что вы понаписали структур, по которым не понять инициализированы они или нет -- так кто же вам доктор-то?
кто только их не понаписал за 40-то лет.
E>Ксати, agregate initialization при таком подходе -- чудо, как хороша E>типа, вместо
struct MySuperStruct xxx;
пишите
MySuperStruct xxx = { 0 };
E>А в то, что состояние структуры "все поля 0" может требовать что-то ещё освобоить я не верю
Ну, для файлов скажем это не 0, а -1.
А дальше можно отправиться к примеру в man pthread_spin_init и там прочитать:
If the pthread_spin_init(pthread_spinlock_t *lock) function fails, the lock is not initialized and the contents of lock are undefined.