Здравствуйте, sts, Вы писали:
sts>Всем привет.
sts>Вопрос как лучше обеспечить освобождение ресурсов в C ? sts>Ниже на вскидку 3 способа. sts>foo1 — просто копипастим освобождение ресурсов. sts>foo2 — заменяем копипасту на goto на код освобождения. sts>foo3 — заменяем похожие if-ы на макрос.
я в голых сях не силён, однако могу высказать свое мнение:
в голых сях точка выхода из функции должна быть одна (кстати, полезную ссылочку нашел : http://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement , но там не про голый си)
перед ней идет освобождение всех ресурсов
плодить метки не обязательно:
в начале функции объявляем все ресурсы
в конце функции просто проверяем каждый из них и при необходимости освобождаем
пример:
int foo2() {
my_resource1_t h1;
my_resource2_t h2;
my_resource3_t h3;
my_resource4_t h4;
int retcode;
/* выделяем ресурс 1 */
retcode = my_resource1_init(&h1);
if (retcode != OK) {
goto finish;
}
/* выделяем ресурс 2 */
retcode = my_resource2_init(&h2);
if (retcode != OK) {
goto finish;
}
/* выделяем ресурс 3 */
retcode = my_resource3_init(&h3);
if (retcode != OK) {
goto finish;
}
/* выделяем ресурс 4 */
retcode = my_resource4_init(&h4);
if (retcode != OK) {
goto finish;
}
/* выполняем некоторые полезные действия */
retcode = do_work_1();
if (retcode != OK) {
goto finish;
}
retcode = do_work_2();
if (retcode != OK) {
goto finish;
}
finish:
if (is_inited(&h1) {
my_resource1_fin(&h1);
}
if (is_inited(&h2) {
my_resource2_fin(&h2);
}
if (is_inited(&h3) {
my_resource3_fin(&h3);
}
if (is_inited(&h4) {
my_resource4_fin(&h4);
}
return retcode;
}
sts>Все способы, как кажется, — жесть какая-то
дело вкуса, ко всему можно привыкнуть, тем более, что этому динозавру много лет, проектов написали много
Можно так ещё сделать:
— все ресурсы инициализируются неким сигнальным значением (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>Вопрос как лучше обеспечить освобождение ресурсов в 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:
[...]
Получается, что на каждый вызов приходится один уровень вложенности.
Экраны сейчас конечно широкие, но все равно уж слишком как-то.
Сама суть оказывается за правым краем монитора
Мне goto больше нравится.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, sts, Вы писали:
sts>>Вопрос как лучше обеспечить освобождение ресурсов в C ?
I>Я использую следующий подход:
I>
Здравствуйте, Кодт, Вы писали:
К>А плох, соответственно, некоторым оверхедом в конце программы.
Это ведь не просто овехед, это еще и перенос логики из времени компиляции во время исполнения. Кроме того (или может быть в частности) двухстадийная инициализация ресурса не дает возможности объявить его const.
//все ресурсы в начальном состоянии, обычно все в NULL, если нет возможности определить по ресурсу был он инициализирован или нет, то это уже ахтунг:) такое я очень не люблю, например когда my_resource1_t это структура а не указатель на структуру
my_resource1_t h1 = NULL;
my_resource2_t h2 = NULL;
my_resource3_t h3 = NULL;
my_resource4_t h4 = NULL;
TRY(res)
{
THROW_RES( my_resource1_init(&h1) );
THROW_RES( my_resource2_init(&h2) );
h3 = malloc(sizeof(my_resource3_t ));
if( !h3 )
THROW( RES_NO_MEMORY );
THROW_RES( my_resource4_init(&h4) );
...
}
CATCH
{
// в res хранится код ошибки
// освобождаем все ресурсы которые мы успели создать, например:if( h1 != NULL )
my_resource1_fin( &h1 );
return res;
}
TRY_END;
return RES_NO_ERROR;
Только надо учесть, что у нас жесткая сигнатура всех функций в плане возвращаемого значения, это сильно упрощает жизнь.
Раньше тоже использовал goto как примерно у вас, но с исключениями мне больше нравится — как-то нагляднее, удобнее и логичнее
Re: [PURE C] Почему замена RAII, а не реализация???
Писал из головы, мог накосячить. Отадку и переделку в том направении, чтобы уйти от аллокации на каждый push оставляю благодарному читателю...
Да, кстати, поверх этой хрени легко приделать и исклчения на чём-то воде лонгджампа...
Ну и вообще многие запары с владением, исключениями и прочей ерундой и в С++ разруливаются.
Скажем можно жить на каком-то отдельном аллокаторе, то, что надо таки рушить, регить в RaiiSupport'е, а в конце большого алгоритма таком или ином, звать RaiiRallback до 0 и рушить аллокатор целиком...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, sts, Вы писали:
sts>Насколько я понял,там предлагается на каждый выделенный ресурс (там он один) создавать вложенный вызов ? sts>Если ресурсов много — совсем криво получается.
Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.
Здравствуйте, 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>
sts>может так получиться, что для 1-го получилось выделить, а для 2-го — нет. sts>Тогда удалять нужно только 1-й.
Тогда второй будет 0...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, sts, Вы писали:
sts>Потому нужно определить сто такое для каждой 0 и как ее выставить в 0. sts>т.е. +2 функции к каждому типу.
В С типов нет... Есть структуры.
Скажем есть у теюя струтктура MyArray, и у неё есть поле body. Проверяешь его и всё...
Ну и вообще чё за страсть писать на С, как на С++? "pp" к расширению дописать ломает что ли?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
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" может требовать что-то ещё освобоить я не верю
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: [PURE C] Почему замена RAII, а не реализация???
Здравствуйте, sts, Вы писали:
sts>>>Мне эти подходы не особо нравятся. E>>А чем goto error_N плох?
sts>как минимум при изменениях кода едет нумерация меток. sts>да и витиевато выходит.
А! Понял. Обычно в рамках этого подхода меткам стараются давать осмысленные названия. Типа там "выход после того", "выход после сего".
А если не могут, то нумеруют, но номера никакого смысла не имеют. Номера могут идти в любом поряке...
sts>тут уже предложили ++stage с последующим switch (stage) для решения этой проблемы. sts>но, кажется, при этом становится еще витиеватее
IMHO это ожнозначно хуже.
Тогда уж лучше завести слово битов и взводить биты, соответствующие тем переменным, которые уже построены...
А эти ++ -- это одноначно дорога в ад.
E>>>>Но если вы жить не можете без RAII, ну заведите себе RAII!
sts>кто только их не понаписал за 40-то лет.
sts>Ну, для файлов скажем это не 0, а -1.
Ну и что? sts>Понаписали вот. Теперь мучайся
Ну флажок рядом клади...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Вопрос как лучше обеспечить освобождение ресурсов в C ?
Ниже на вскидку 3 способа.
foo1 — просто копипастим освобождение ресурсов.
foo2 — заменяем копипасту на goto на код освобождения.
foo3 — заменяем похожие if-ы на макрос.
ну и так далее: fooX — дальше плодим макросы (какие ?)
Все способы, как кажется, — жесть какая-то
Пусть даже 3-й выглядит боле-менее читаемым, но вся эта возня с нумерацией меток + часто возникает необходимость доп. анализа кодов возврата и тогда макросы уже не особо помогают, все равно получается много if-ов. И при добавлении ресурсов нумерация съезжает.
Кто как выходит из ситуации ?
Какие есть адекватные способы ?
Желательно достаточно простые.
Т.е. понятно, что можно реализовать RAII на макросах, но почему-то не хочется
(С++ — не предлагать, нужно решить задачу в рамках C.)
Здравствуйте, uzhas, Вы писали:
U>в голых сях точка выхода из функции должна быть одна (кстати, полезную ссылочку нашел : http://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement , но там не про голый си) U>перед ней идет освобождение всех ресурсов U>плодить метки не обязательно: U>в начале функции объявляем все ресурсы U>в конце функции просто проверяем каждый из них и при необходимости освобождаем
Спасибо за ссылку.
Есть проблема с проверкой инициализирована переменная или нет — в дополнение к конструктору и деструктору еще нужна как минимум та самая функция проверки для каждого типа + обнуление в начале, до конструктора.
Я пока склоняюсь к вложенным if-ам, но тоже получается жесткач в плане читабельности.
Здравствуйте, sts, Вы писали:
sts>Всем привет.
sts>Вопрос как лучше обеспечить освобождение ресурсов в C ? sts>Ниже на вскидку 3 способа. sts>foo1 — просто копипастим освобождение ресурсов. sts>foo2 — заменяем копипасту на goto на код освобождения. sts>foo3 — заменяем похожие if-ы на макрос. sts>ну и так далее: fooX — дальше плодим макросы (какие ?)
Здравствуйте, sts, Вы писали:
sts>Есть проблема с проверкой инициализирована переменная или нет — в дополнение к конструктору и деструктору еще нужна как минимум та самая функция проверки для каждого типа + обнуление в начале, до конструктора. sts>Я пока склоняюсь к вложенным if-ам, но тоже получается жесткач в плане читабельности.
Здравствуйте, barmafon, Вы писали:
B>Здравствуйте, sts, Вы писали:
sts>>Есть проблема с проверкой инициализирована переменная или нет — в дополнение к конструктору и деструктору еще нужна как минимум та самая функция проверки для каждого типа + обнуление в начале, до конструктора. sts>>Я пока склоняюсь к вложенным if-ам, но тоже получается жесткач в плане читабельности.
B>Например, так
Насколько я понял,там предлагается на каждый выделенный ресурс (там он один) создавать вложенный вызов ?
Если ресурсов много — совсем криво получается.
Здравствуйте, 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>
) ввести доп. переменную stage и инкрементить ее в макросе THROW_RES.
А потом в CATCH ее анализировать (тут не понятно пока какой синтаксис будет).
Или у вас как-то иначе реализовано ?
Здравствуйте, abdab, Вы писали:
A>Здравствуйте, sts, Вы писали:
A>У нас реализована простейшая система исключений, без вложенности, без использования кучи или глобальных переменных, вот такая:
A>
A>#define TRY(code) {\
A> jmp_buf __jbuf; \
A> result_t code = RES_NO_ERROR; \
A> result_t __exc_res = RES_NO_ERROR; \
A> (void)code; \
A> (void)__exc_res; \
A> switch ( code = setjmp(__jbuf) ) \
A> if(0) case 0:
A>#define CATCH \
A> else default:
A>#define TRY_END ;}
A>#define THROW(code) \
A> longjmp(__jbuf, code)
A>#define THROW_RES(f) { if( (__exc_res = (f)) != RES_NO_ERROR ) THROW( __exc_res ); }
A>
A>и примерно вот так потом используется:
A>
A>//все ресурсы в начальном состоянии, обычно все в NULL, если нет возможности определить по ресурсу был он инициализирован или нет, то это уже ахтунг:) такое я очень не люблю, например когда my_resource1_t это структура а не указатель на структуру
A>my_resource1_t h1 = NULL;
A>my_resource2_t h2 = NULL;
A>my_resource3_t h3 = NULL;
A>my_resource4_t h4 = NULL;
A>TRY(res)
A> {
A> THROW_RES( my_resource1_init(&h1) );
A> THROW_RES( my_resource2_init(&h2) );
A> h3 = malloc(sizeof(my_resource3_t ));
A> if( !h3 )
A> THROW( RES_NO_MEMORY );
A> THROW_RES( my_resource4_init(&h4) );
A> ...
A> }
A> CATCH
A> {
A> // в res хранится код ошибки
A> // освобождаем все ресурсы которые мы успели создать, например:
A> if( h1 != NULL )
A> my_resource1_fin( &h1 );
A> return res;
A> }
A> TRY_END;
A>return RES_NO_ERROR;
A>
A>Только надо учесть, что у нас жесткая сигнатура всех функций в плане возвращаемого значения, это сильно упрощает жизнь. A>Раньше тоже использовал goto как примерно у вас, но с исключениями мне больше нравится — как-то нагляднее, удобнее и логичнее
Здравствуйте, hotdox, Вы писали:
H>Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.
А как определить какой ресурс нужно удалять, а какой нет ?
Т.е. как может выглядеть обобшение на несколько ресурсов ?
Здравствуйте, sts, Вы писали:
sts>Здравствуйте, hotdox, Вы писали:
H>>Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.
sts>А как определить какой ресурс нужно удалять, а какой нет ? sts>Т.е. как может выглядеть обобшение на несколько ресурсов ?
Удалять надо все, ведь вначале для всех выделяем память.
Здравствуйте, hotdox, Вы писали:
H>Здравствуйте, sts, Вы писали:
sts>>Здравствуйте, hotdox, Вы писали:
H>>>Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.
sts>>А как определить какой ресурс нужно удалять, а какой нет ? sts>>Т.е. как может выглядеть обобшение на несколько ресурсов ? H>Удалять надо все, ведь вначале для всех выделяем память.
может так получиться, что для 1-го получилось выделить, а для 2-го — нет.
Тогда удалять нужно только 1-й.
Здравствуйте, sts, Вы писали:
sts>Получается, что на каждый вызов приходится один уровень вложенности.
Это не просто уровень вложенности, он показывает логику программы. Если этих уровней штук сто, можно подумать не применить ли нетрадиционное форматирование:
Но обычно логика программы подсказывает также как разложить одну большую функцию на несколько меньших, так что проблема ста уровней вложенности не возникает.
Здравствуйте, sts, Вы писали:
sts>Тут не понятно только как решается проблема с тем, что одни ресурсы нужно финализировать, а другие — нет. sts>Можно например (как здесь
) ввести доп. переменную stage и инкрементить ее в макросе THROW_RES. sts>А потом в CATCH ее анализировать (тут не понятно пока какой синтаксис будет). sts>Или у вас как-то иначе реализовано ?
Ну я ж и говорю, что по ресурсу должно быть видно инициализирован он или нет, поэтому в CATCH просто проверям каждый ресурс и если он создан, финализируем его.
Примерно вот так:
Здравствуйте, abdab, Вы писали:
A>Здравствуйте, sts, Вы писали:
sts>>Тут не понятно только как решается проблема с тем, что одни ресурсы нужно финализировать, а другие — нет. sts>>Можно например (как здесь
) ввести доп. переменную stage и инкрементить ее в макросе THROW_RES. sts>>А потом в CATCH ее анализировать (тут не понятно пока какой синтаксис будет). sts>>Или у вас как-то иначе реализовано ?
A>Ну я ж и говорю, что по ресурсу должно быть видно инициализирован он или нет, поэтому в CATCH просто проверям каждый ресурс и если он создан, финализируем его. A>Примерно вот так: A>
A>if( h1 != NULL )
A> my_resource1_fin( &h1 );
A>if( h2 != NULL )
A> my_resource2_fin( &h2 );
A>и т.д.
A>
A>Я, например, не вижу минусов такой практики, буду признателен, если кто-нибудь укажет на таковые.
Мне видятся такие:
— необходимо иметь функцию установки в null, которая отрабатывает ДО конструктора, ну или совместить ее с конструктором; тогда тот должен четко разделять 2 фазы конструирования;
— необходимо иметь функцию проверки на null, которую нужно звать ДО деструктора, или доп.требование к деструктору — не удалять не инициализированные объекты.
Код по фазам выглядит иначе:
my_type_t h;
my_type_clear(&h);
int rc = my_type_init(&h);
...
if (!my_type_is_null(&h)) {
my_type_fin(&h);
}
Здравствуйте, sts, Вы писали:
sts>Вопрос как лучше обеспечить освобождение ресурсов в C ?
sts>Все способы, как кажется, — жесть какая-то sts>Пусть даже 3-й выглядит боле-менее читаемым, но вся эта возня с нумерацией меток + часто возникает необходимость доп. анализа кодов возврата и тогда макросы уже не особо помогают, все равно получается много if-ов. И при добавлении ресурсов нумерация съезжает. sts>Кто как выходит из ситуации ? sts>Какие есть адекватные способы ? sts>Желательно достаточно простые. sts>Т.е. понятно, что можно реализовать RAII на макросах, но почему-то не хочется sts>(С++ — не предлагать, нужно решить задачу в рамках C.)
думаю, можно реализовать что-то такое:
struct foo {
};
void foo_construct ( foo *, int );
void foo_construct_default ( foo * );
void foo_destruct ( foo * );
struct bar {
};
void bar_construct_default ( bar * );
void bar_destruct ( bar * );
void exec () { THROW; }
void test ()
{
foo * x = 0;
bar y = {};
TRY {
x = malloc( sizeof( foo ) );
CONSTRUCT( foo, x, construct( x, 1 ) );
CONSTRUCT( bar, &y );
exec();
DESTRUCT( bar, &y );
DESTRUCT( foo, x );
} CATCH {
THROW;
} FINALLY {
free( x );
} END_TRY;
}
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, sts, Вы писали:
sts>>Вопрос как лучше обеспечить освобождение ресурсов в C ?
sts>>Все способы, как кажется, — жесть какая-то sts>>Пусть даже 3-й выглядит боле-менее читаемым, но вся эта возня с нумерацией меток + часто возникает необходимость доп. анализа кодов возврата и тогда макросы уже не особо помогают, все равно получается много if-ов. И при добавлении ресурсов нумерация съезжает. sts>>Кто как выходит из ситуации ? sts>>Какие есть адекватные способы ? sts>>Желательно достаточно простые. sts>>Т.е. понятно, что можно реализовать RAII на макросах, но почему-то не хочется sts>>(С++ — не предлагать, нужно решить задачу в рамках C.)
NB>думаю, можно реализовать что-то такое: NB>
NB>struct foo {
NB>};
NB>void foo_construct ( foo *, int );
NB>void foo_construct_default ( foo * );
NB>void foo_destruct ( foo * );
NB>struct bar {
NB>};
NB>void bar_construct_default ( bar * );
NB>void bar_destruct ( bar * );
NB>void exec () { THROW; }
NB>void test ()
NB>{
NB> foo * x = 0;
NB> bar y = {};
NB> TRY {
NB> x = malloc( sizeof( foo ) );
NB> CONSTRUCT( foo, x, construct( x, 1 ) );
NB> CONSTRUCT( bar, &y );
NB> exec();
NB> DESTRUCT( bar, &y );
NB> DESTRUCT( foo, x );
NB> } CATCH {
NB> THROW;
NB> } FINALLY {
NB> free( x );
NB> } END_TRY;
NB>}
NB>
Наверное можно.
Только не понятно что такое TRY CONSTRUCT DESTRUCT CATCH THROW FINALLY END_TRY и т.д.
Здравствуйте, sts, Вы писали:
sts>Мне видятся такие: sts>- необходимо иметь функцию установки в null, которая отрабатывает ДО конструктора, ну или совместить ее с конструктором; тогда тот должен четко разделять 2 фазы конструирования; sts>- необходимо иметь функцию проверки на null, которую нужно звать ДО деструктора, или доп.требование к деструктору — не удалять не инициализированные объекты.
Не совсем понятно, что вы подразумеваете по конструктором/деструктором в С.
Все начальные значения ресурсов устанавливаются перед инициализацией. Собсно вот то же самое, что я имею в виду: http://www.rsdn.ru/forum/cpp/4500007.1.aspx
Здравствуйте, abdab, Вы писали:
A>Здравствуйте, sts, Вы писали:
sts>>Мне видятся такие: sts>>- необходимо иметь функцию установки в null, которая отрабатывает ДО конструктора, ну или совместить ее с конструктором; тогда тот должен четко разделять 2 фазы конструирования; sts>>- необходимо иметь функцию проверки на null, которую нужно звать ДО деструктора, или доп.требование к деструктору — не удалять не инициализированные объекты.
A>Не совсем понятно, что вы подразумеваете по конструктором/деструктором в С. A>Все начальные значения ресурсов устанавливаются перед инициализацией. Собсно вот то же самое, что я имею в виду: http://www.rsdn.ru/forum/cpp/4500007.1.aspx
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, sts, Вы писали:
К>Можно так ещё сделать: К>- все ресурсы инициализируются неким сигнальным значением (NULL'ом, например) К>- по ходу работы, выполняются захваты ресурсов, а их переменные переходят из сигнального значения в рабочее К>- по завершении, будь то обычное или аварийное, освобождаются все ресурсы, содержащие рабочее значение
все хорошо пока ресурс — это простой тип — число или указатель.
с предопределенными структурами уже хуже получается.
Здравствуйте, 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 и рушить аллокатор целиком...
Здравствуйте, 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.
Понаписали вот. Теперь мучайся
Re[5]: [PURE C] Почему замена RAII, а не реализация???
E>>А в то, что состояние структуры "все поля 0" может требовать что-то ещё освобоить я не верю sts>Ну, для файлов скажем это не 0, а -1.
Значит, потребуется явная инициализация не-нулём.
sts>А дальше можно отправиться к примеру в man pthread_spin_init и там прочитать: sts>
sts> If the pthread_spin_init(pthread_spinlock_t *lock) function fails, the lock is not initialized and the contents of lock are undefined.
sts>Понаписали вот. Теперь мучайся
На это надо заводить отдельную переменную типа lock_inited. Поставить её в 0 в общем начале и в 1 при успешной инициализации, мягко говоря, не проблема (по сравнению с общей задачей)
Здравствуйте, igna, Вы писали:
I>То есть твое "в С типов нет" было не по делу?
То есть не можешь что-то сказать по теме топика? -- не флуди...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>То есть не можешь что-то сказать по теме топика? -- не флуди...
Спрашивающему-то я ответил подробно. А пройти мимо, когда говорят неправду, можно конечно, но в данном случае не нужно. Что это тебя так заело? Ну сказал глупость, ну поправили тебя, так на то и форум.
я бы для каждой структуры написал пару foo_ctor/foo_dtor.
внутри можно четко проинициализировать, проверить и освободить.
еще вариант с goto хорош. вот только никаких stages, только именные метки.
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, sts, Вы писали:
КЛ>[]
КЛ>давно ничего не писал сюда.
КЛ>я бы для каждой структуры написал пару foo_ctor/foo_dtor. КЛ>внутри можно четко проинициализировать, проверить и освободить. КЛ>еще вариант с goto хорош. вот только никаких stages, только именные метки.
Прижилось разделение кода функции на 2 части — которя выделяет-удаляет ресурсы и вызывает функцию реализующую алгоритм передавая ей готовые ресурсы в параметрах. С именными метками тоже не очень получается, пока рулят флажки xxx_initialized и goto на единственную метку где эти флажки проверяются.
А вообще получается, что единый поход (кроме своего RAII, как в YOYO) выработать сложно. В каждом конкретном случае свой подход рулит.