[PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 09:19
Оценка:
Всем привет.

Вопрос как лучше обеспечить освобождение ресурсов в C ?
Ниже на вскидку 3 способа.
foo1 — просто копипастим освобождение ресурсов.
foo2 — заменяем копипасту на goto на код освобождения.
foo3 — заменяем похожие if-ы на макрос.
ну и так далее: fooX — дальше плодим макросы (какие ?)

Все способы, как кажется, — жесть какая-то
Пусть даже 3-й выглядит боле-менее читаемым, но вся эта возня с нумерацией меток + часто возникает необходимость доп. анализа кодов возврата и тогда макросы уже не особо помогают, все равно получается много if-ов. И при добавлении ресурсов нумерация съезжает.
Кто как выходит из ситуации ?
Какие есть адекватные способы ?
Желательно достаточно простые.
Т.е. понятно, что можно реализовать RAII на макросах, но почему-то не хочется
(С++ — не предлагать, нужно решить задачу в рамках C.)

int foo1() {
    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) {
        return retcode;
    }
    /* выделяем ресурс 2 */
    retcode = my_resource2_init(&h2);
    if (retcode != OK) {
        my_resource1_fin(&h1);
        return retcode;
    }
    /* выделяем ресурс 3 */
    retcode = my_resource3_init(&h3);
    if (retcode != OK) {
        my_resource2_fin(&h2);
        my_resource1_fin(&h1);
        return retcode;
    }
    /* выделяем ресурс 4 */
    retcode = my_resource4_init(&h4);
    if (retcode != OK) {
        my_resource3_fin(&h3);
        my_resource2_fin(&h2);
        my_resource1_fin(&h1);
        return retcode;
    }

    /* выполняем некоторые полезные действия */
    retcode = do_work_1();
    if (retcode != OK) {
        my_resource4_fin(&h4);
        my_resource3_fin(&h3);
        my_resource2_fin(&h2);
        my_resource1_fin(&h1);
        return retcode;
    }
    retcode = do_work_2();
    if (retcode != OK) {
        my_resource4_fin(&h4);
        my_resource3_fin(&h3);
        my_resource2_fin(&h2);
        my_resource1_fin(&h1);
        return retcode;
    }

    my_resource4_fin(&h4);
    my_resource3_fin(&h3);
    my_resource2_fin(&h2);
    my_resource1_fin(&h1);

    return OK;
}

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 error0;
    }
    /* выделяем ресурс 2 */
    retcode = my_resource2_init(&h2);
    if (retcode != OK) {
        goto error1;
    }
    /* выделяем ресурс 3 */
    retcode = my_resource3_init(&h3);
    if (retcode != OK) {
        goto error2;
    }
    /* выделяем ресурс 4 */
    retcode = my_resource4_init(&h4);
    if (retcode != OK) {
        goto error3;
    }

    /* выполняем некоторые полезные действия */
    retcode = do_work_1();
    if (retcode != OK) {
        goto error4;
    }
    retcode = do_work_2();
    if (retcode != OK) {
        goto error4;
    }

error4:
    my_resource4_fin(&h4);
error3:
    my_resource3_fin(&h3);
error2:
    my_resource2_fin(&h2);
error1:
    my_resource1_fin(&h1);
error0:
    return retcode;
}

#define CHECK(n, expr) \
    retcode = (expr);\
    if (retcode != OK) {\
        goto error##n;\
    }
int foo3() {
    my_resource1_t h1;
    my_resource2_t h2;
    my_resource3_t h3;
    my_resource4_t h4;

    /* выделяем ресурс 1 */
    CHECK(0, my_resource1_init(&h1));
    /* выделяем ресурс 2 */
    CHECK(1, my_resource2_init(&h2));
    /* выделяем ресурс 3 */
    CHECK(2, my_resource3_init(&h3));
    /* выделяем ресурс 4 */
    CHECK(3, my_resource4_init(&h4));

    /* выполняем некоторые полезные действия */
    CHECK(4, do_work_1());
    CHECK(4, do_work_2());

error4:
    my_resource4_fin(&h4);
error3:
    my_resource3_fin(&h3);
error2:
    my_resource2_fin(&h2);
error1:
    my_resource1_fin(&h1);
error0:
    return retcode;
}
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: uzhas Ниоткуда  
Дата: 16.11.11 09:45
Оценка: 6 (1) +2
Здравствуйте, 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>Все способы, как кажется, — жесть какая-то

дело вкуса, ко всему можно привыкнуть, тем более, что этому динозавру много лет, проектов написали много
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 10:00
Оценка:
Здравствуйте, uzhas, Вы писали:

U>в голых сях точка выхода из функции должна быть одна (кстати, полезную ссылочку нашел : http://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement , но там не про голый си)

U>перед ней идет освобождение всех ресурсов
U>плодить метки не обязательно:
U>в начале функции объявляем все ресурсы
U>в конце функции просто проверяем каждый из них и при необходимости освобождаем

Спасибо за ссылку.

Есть проблема с проверкой инициализирована переменная или нет — в дополнение к конструктору и деструктору еще нужна как минимум та самая функция проверки для каждого типа + обнуление в начале, до конструктора.

Я пока склоняюсь к вложенным if-ам, но тоже получается жесткач в плане читабельности.
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: hotdox  
Дата: 16.11.11 10:05
Оценка:
Здравствуйте, sts, Вы писали:

sts>Всем привет.


sts>Вопрос как лучше обеспечить освобождение ресурсов в C ?

sts>Ниже на вскидку 3 способа.
sts>foo1 — просто копипастим освобождение ресурсов.
sts>foo2 — заменяем копипасту на goto на код освобождения.
sts>foo3 — заменяем похожие if-ы на макрос.
sts>ну и так далее: fooX — дальше плодим макросы (какие ?)

Мне придумалось как-то так: http://whalebot.blogspot.com/2010/10/raii-in-c.html

Вкратце:
do_smth(void *res1, void *res2)

do_smth_raii()
{
    //init
    res1 = malloc(...);
    res2 = malloc(...);
    do_smth(res1, res2);
    free(res1);
    free(res2);
}
Re[3]: [PURE C] Закат Солнца вручную: замена RAII ???
От: barmafon  
Дата: 16.11.11 10:16
Оценка:
Здравствуйте, sts, Вы писали:

sts>Есть проблема с проверкой инициализирована переменная или нет — в дополнение к конструктору и деструктору еще нужна как минимум та самая функция проверки для каждого типа + обнуление в начале, до конструктора.

sts>Я пока склоняюсь к вложенным if-ам, но тоже получается жесткач в плане читабельности.

Например, так
Автор: barmafon
Дата: 18.06.04
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: abdab Россия  
Дата: 16.11.11 10:23
Оценка: 6 (1)
Здравствуйте, sts, Вы писали:

У нас реализована простейшая система исключений, без вложенности, без использования кучи или глобальных переменных, вот такая:

#define TRY(code)  {\
  jmp_buf __jbuf; \
  result_t  code = RES_NO_ERROR; \
  result_t __exc_res = RES_NO_ERROR; \
  (void)code; \
  (void)__exc_res; \
  switch ( code = setjmp(__jbuf) ) \
    if(0) case 0:

#define CATCH  \
    else default:

#define TRY_END  ;}

#define THROW(code) \
    longjmp(__jbuf, code)

#define THROW_RES(f) { if( (__exc_res = (f)) != RES_NO_ERROR ) THROW( __exc_res ); }


и примерно вот так потом используется:

//все ресурсы в начальном состоянии, обычно все в 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[4]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 11:02
Оценка:
Здравствуйте, barmafon, Вы писали:

B>Здравствуйте, sts, Вы писали:


sts>>Есть проблема с проверкой инициализирована переменная или нет — в дополнение к конструктору и деструктору еще нужна как минимум та самая функция проверки для каждого типа + обнуление в начале, до конструктора.

sts>>Я пока склоняюсь к вложенным if-ам, но тоже получается жесткач в плане читабельности.

B>Например, так
Автор: barmafon
Дата: 18.06.04


Боле-менее красиво пока между выделением ресурсов не втыкаются вызовы других функций, т.е. сама логика.
Иначе тоже опять можно в цифрах потеряться.
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 11:03
Оценка:
Насколько я понял,там предлагается на каждый выделенный ресурс (там он один) создавать вложенный вызов ?
Если ресурсов много — совсем криво получается.


Здравствуйте, 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>
H>do_smth(void *res1, void *res2)

H>do_smth_raii()
H>{
H>    //init
H>    res1 = malloc(...);
H>    res2 = malloc(...);
H>    do_smth(res1, res2);
H>    free(res1);
H>    free(res2);
H>}
H>
йй
Re[3]: [PURE C] Закат Солнца вручную: замена RAII ???
От: hotdox  
Дата: 16.11.11 11:11
Оценка: 2 (1)
Здравствуйте, sts, Вы писали:

sts>Насколько я понял,там предлагается на каждый выделенный ресурс (там он один) создавать вложенный вызов ?

sts>Если ресурсов много — совсем криво получается.


Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 11:14
Оценка:
Тут не понятно только как решается проблема с тем, что одни ресурсы нужно финализировать, а другие — нет.
Можно например (как здесь
Автор: sts
Дата: 16.11.11
) ввести доп. переменную 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 как примерно у вас, но с исключениями мне больше нравится — как-то нагляднее, удобнее и логичнее
Re[4]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 11:16
Оценка:
Здравствуйте, hotdox, Вы писали:

H>Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.


А как определить какой ресурс нужно удалять, а какой нет ?
Т.е. как может выглядеть обобшение на несколько ресурсов ?
Re[5]: [PURE C] Закат Солнца вручную: замена RAII ???
От: hotdox  
Дата: 16.11.11 11:35
Оценка:
Здравствуйте, sts, Вы писали:

sts>Здравствуйте, hotdox, Вы писали:


H>>Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.


sts>А как определить какой ресурс нужно удалять, а какой нет ?

sts>Т.е. как может выглядеть обобшение на несколько ресурсов ?
Удалять надо все, ведь вначале для всех выделяем память.
Re[6]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 11:36
Оценка:
Здравствуйте, hotdox, Вы писали:

H>Здравствуйте, sts, Вы писали:


sts>>Здравствуйте, hotdox, Вы писали:


H>>>Предлагается память для всех временных ресурсов выделять и освобождать в одном месте, отделить управление памятью от логики исполнения. Проблемы возникают, когда у временных ресурсов разное время жизни. А если время жизни одинаковое, то достаточно одной обертки.


sts>>А как определить какой ресурс нужно удалять, а какой нет ?

sts>>Т.е. как может выглядеть обобшение на несколько ресурсов ?
H>Удалять надо все, ведь вначале для всех выделяем память.

может так получиться, что для 1-го получилось выделить, а для 2-го — нет.
Тогда удалять нужно только 1-й.
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: igna Россия  
Дата: 16.11.11 11:50
Оценка: 7 (2) +1 :)
Здравствуйте, sts, Вы писали:

sts>Вопрос как лучше обеспечить освобождение ресурсов в C ?


Я использую следующий подход:

int foo1() {
    int retcode;

    my_resource1_t h1;
    retcode = my_resource1_init(&h1);
    if (retcode == OK) {

        my_resource2_t h2;
        retcode = my_resource2_init(&h2);
        if (retcode == OK) {

            my_resource3_t h3;
            retcode = my_resource3_init(&h3);
            if (retcode == OK) {

                my_resource4_t h4;
                retcode = my_resource4_init(&h4);
                if (retcode == OK) {

                    retcode = do_work_1();
                    if (retcode == OK)
                        retcode = do_work_2();
                    my_resource4_fin(&h4);
                }
                my_resource3_fin(&h3);
            }
            my_resource2_fin(&h2);
        }
        my_resource1_fin(&h1);
    }
    return retcode;
}
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 12:10
Оценка: +2
Получается, что на каждый вызов приходится один уровень вложенности.
Экраны сейчас конечно широкие, но все равно уж слишком как-то.
Сама суть оказывается за правым краем монитора

Мне goto больше нравится.

Здравствуйте, igna, Вы писали:

I>Здравствуйте, sts, Вы писали:


sts>>Вопрос как лучше обеспечить освобождение ресурсов в C ?


I>Я использую следующий подход:


I>
I>int foo1() {
I>    int retcode;

I>    my_resource1_t h1;
I>    retcode = my_resource1_init(&h1);
I>    if (retcode == OK) {

I>        my_resource2_t h2;
I>        retcode = my_resource2_init(&h2);
I>        if (retcode == OK) {

I>            my_resource3_t h3;
I>            retcode = my_resource3_init(&h3);
I>            if (retcode == OK) {

I>                my_resource4_t h4;
I>                retcode = my_resource4_init(&h4);
I>                if (retcode == OK) {

I>                    retcode = do_work_1();
I>                    if (retcode == OK)
I>                        retcode = do_work_2();
I>                    my_resource4_fin(&h4);
I>                }
I>                my_resource3_fin(&h3);
I>            }
I>            my_resource2_fin(&h2);
I>        }
I>        my_resource1_fin(&h1);
I>    }
I>    return retcode;
I>}
I>
Re[3]: [PURE C] Закат Солнца вручную: замена RAII ???
От: igna Россия  
Дата: 16.11.11 12:19
Оценка:
Здравствуйте, sts, Вы писали:

sts>Получается, что на каждый вызов приходится один уровень вложенности.


Это не просто уровень вложенности, он показывает логику программы. Если этих уровней штук сто, можно подумать не применить ли нетрадиционное форматирование:

int foo1() {
    int retcode;

    my_resource1_t h1;
    retcode = my_resource1_init(&h1);
    if (retcode == OK) {

    my_resource2_t h2;
    retcode = my_resource2_init(&h2);
    if (retcode == OK) {

    my_resource3_t h3;
    retcode = my_resource3_init(&h3);
    if (retcode == OK) {

    my_resource4_t h4;
    retcode = my_resource4_init(&h4);
    if (retcode == OK) {

    . . .

    my_resource100_t h100;
    retcode = my_resource100_init(&h100);
    if (retcode == OK) {

        retcode = do_work_1();
        if (retcode == OK)
            retcode = do_work_2();

    my_resource100_fin(&h100);
    }

    . . .

    my_resource4_fin(&h4);
    }
    my_resource3_fin(&h3);
    }
    my_resource2_fin(&h2);
    }
    my_resource1_fin(&h1);
    }
    return retcode;
}


Но обычно логика программы подсказывает также как разложить одну большую функцию на несколько меньших, так что проблема ста уровней вложенности не возникает.
Re[3]: [PURE C] Закат Солнца вручную: замена RAII ???
От: abdab Россия  
Дата: 16.11.11 15:00
Оценка:
Здравствуйте, sts, Вы писали:

sts>Тут не понятно только как решается проблема с тем, что одни ресурсы нужно финализировать, а другие — нет.

sts>Можно например (как здесь
Автор: sts
Дата: 16.11.11
) ввести доп. переменную stage и инкрементить ее в макросе THROW_RES.

sts>А потом в CATCH ее анализировать (тут не понятно пока какой синтаксис будет).
sts>Или у вас как-то иначе реализовано ?

Ну я ж и говорю, что по ресурсу должно быть видно инициализирован он или нет, поэтому в CATCH просто проверям каждый ресурс и если он создан, финализируем его.
Примерно вот так:
if( h1 != NULL )
   my_resource1_fin( &h1 );
if( h2 != NULL )
   my_resource2_fin( &h2 );
и т.д.


Я, например, не вижу минусов такой практики, буду признателен, если кто-нибудь укажет на таковые.
Re[4]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 16:03
Оценка:
Здравствуйте, abdab, Вы писали:

A>Здравствуйте, sts, Вы писали:


sts>>Тут не понятно только как решается проблема с тем, что одни ресурсы нужно финализировать, а другие — нет.

sts>>Можно например (как здесь
Автор: sts
Дата: 16.11.11
) ввести доп. переменную 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);
}
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: night beast СССР  
Дата: 16.11.11 16:47
Оценка:
Здравствуйте, 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;

}
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 16.11.11 17:28
Оценка:
Здравствуйте, 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 и т.д.
Re[3]: [PURE C] Закат Солнца вручную: замена RAII ???
От: night beast СССР  
Дата: 16.11.11 17:30
Оценка:
Здравствуйте, sts, Вы писали:

sts>Наверное можно.

sts>Только не понятно что такое TRY CONSTRUCT DESTRUCT CATCH THROW FINALLY END_TRY и т.д.

это макросы.
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: Кодт Россия  
Дата: 16.11.11 18:02
Оценка: 4 (1) +2
Здравствуйте, 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-ов над интегральными переменными.
Перекуём баги на фичи!
Re[5]: [PURE C] Закат Солнца вручную: замена RAII ???
От: abdab Россия  
Дата: 17.11.11 06:09
Оценка:
Здравствуйте, sts, Вы писали:

sts>Мне видятся такие:

sts>- необходимо иметь функцию установки в null, которая отрабатывает ДО конструктора, ну или совместить ее с конструктором; тогда тот должен четко разделять 2 фазы конструирования;
sts>- необходимо иметь функцию проверки на null, которую нужно звать ДО деструктора, или доп.требование к деструктору — не удалять не инициализированные объекты.

Не совсем понятно, что вы подразумеваете по конструктором/деструктором в С.
Все начальные значения ресурсов устанавливаются перед инициализацией. Собсно вот то же самое, что я имею в виду: http://www.rsdn.ru/forum/cpp/4500007.1.aspx
Автор: Кодт
Дата: 16.11.11
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.11.11 08:02
Оценка: 6 (1) +1
Здравствуйте, 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:
[...]
The God is real, unless declared integer.
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sidorov18 США  
Дата: 17.11.11 08:16
Оценка: :)
Здравствуйте, 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>
H>do_smth(void *res1, void *res2)

H>do_smth_raii()
H>{
H>    //init
H>    res1 = malloc(...);
H>    res2 = malloc(...);
H>    do_smth(res1, res2);
H>    free(res1);
H>    free(res2);
H>}
H>


тут еще проблема в подходе, в стиле WinInet.

Там принцип примерно такой:


HINTERNET hInet = GetInternetHandle(/*куча параметров*/);
HINTERNET hSession = GetSessionHandle(hInet/*+ куча параметров*/);
HINTERNET hRequest = GetRequestHandle(hSession/*+ куча параметров*/);

CloseHandle(hRequest);
CloseHandle(hSession);
CloseHandle(hInet);


для этого можно передавать параметры по двойному указателю и инициализировать их внутри.
а снаружи проверять на валидность и закрывать при надобности.
Re[6]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 17.11.11 08:38
Оценка:
Здравствуйте, abdab, Вы писали:

A>Здравствуйте, sts, Вы писали:


sts>>Мне видятся такие:

sts>>- необходимо иметь функцию установки в null, которая отрабатывает ДО конструктора, ну или совместить ее с конструктором; тогда тот должен четко разделять 2 фазы конструирования;
sts>>- необходимо иметь функцию проверки на null, которую нужно звать ДО деструктора, или доп.требование к деструктору — не удалять не инициализированные объекты.

A>Не совсем понятно, что вы подразумеваете по конструктором/деструктором в С.

A>Все начальные значения ресурсов устанавливаются перед инициализацией. Собсно вот то же самое, что я имею в виду: http://www.rsdn.ru/forum/cpp/4500007.1.aspx
Автор: Кодт
Дата: 16.11.11


xxx_init — конструктор
xxx_fin — деструктор
Re[7]: [PURE C] Закат Солнца вручную: замена RAII ???
От: Erop Россия  
Дата: 17.11.11 09:04
Оценка: :)
Здравствуйте, sts, Вы писали:


sts>может так получиться, что для 1-го получилось выделить, а для 2-го — нет.

sts>Тогда удалять нужно только 1-й.

Тогда второй будет 0...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 17.11.11 09:20
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, sts, Вы писали:



sts>>может так получиться, что для 1-го получилось выделить, а для 2-го — нет.

sts>>Тогда удалять нужно только 1-й.

E>Тогда второй будет 0...


Не указатели, а структуры.
Потому нужно определить сто такое для каждой 0 и как ее выставить в 0.
т.е. +2 функции к каждому типу.
Re: [PURE C] Что если так ?
От: sts  
Дата: 17.11.11 09:38
Оценка:
Что если так сделать ?
Покритикуйте пожалуйста.

#define TRY \
    {\
        int retcode;

#define FINALLY \
    end:

#define END \
    } \
    return retcode;

#define THROW_RETCODE goto end

#define THROW(error) do { retcode = error; THROW_RETCODE; } while(0)

#define RETURN do { retcode = OK; THROW_RETCODE; } while(0)

#define RESOURCE_INIT(type, name, expr) \
    type name; \
    type *name##_ptr = NULL; \
    retcode = (expr); \
    if (retcode != OK) { \
        THROW_RETCODE; \
    } else { \
        name##_ptr = &name; \
    }

#define CHECK_RETCODE(expr)
    retcode = (expr); \
    if (retcode != OK) { \
        THROW_RETCODE; \
    }

#define CHECK(expr) \
    if (!(expr)) { \
        THROW(ERROR); \
    }


#define RESOURCE_FREE(name, destructor) \
    if (name##_ptr) { \
        destructor(name##_ptr); \
    }

int memory_init(void **h, size_t size) {
    *h = malloc(size);
    return *h ? OK : ERROR_NO_MEMORY;
}

void memory_fin(void **h) {
    free(*h);
}

int foo4() {
    TRY
        RESOURCE_INIT(my_resource1_t, h1,  my_resource1_init(&h1));
        RESOURCE_INIT(my_resource2_t, h2,  my_resource1_init(&h2));
        RESOURCE_INIT(my_resource3_t, h3,  my_resource1_init(&h3));
        RESOURCE_INIT(my_resource4_t, h4,  my_resource1_init(&h4));
        RESOURCE_INIT(void*,          mem, memory_init(&mem, 100500));
        int i;
        for (i = 0; i < 100; ++i) {
            int result;
            CHECK_RETCODE(do_work_1(&result));
            if (result < 0) {
                THROW(ERROR);
            }
            if (result == 17) {
                RETURN;
            }
        }
        CHECK_RETCODE(do_work_2());
    FINALLY
        RESOURCE_FREE(mem, memory_fin);
        RESOURCE_FREE(h4,  my_resource4_fin);
        RESOURCE_FREE(h3,  my_resource3_fin);
        RESOURCE_FREE(h2,  my_resource2_fin);
        RESOURCE_FREE(h1,  my_resource1_fin);
    END
}
Re: [PURE C] Почему замена RAII, а не реализация???
От: Erop Россия  
Дата: 17.11.11 09:57
Оценка: 6 (1)
Здравствуйте, sts, Вы писали:

sts>Все способы, как кажется, — жесть какая-то


Обычно на С пишут немного не так, как на С++, так что получается вовсе и не жесть.
Но если вы жить не можете без RAII, ну заведите себе RAII!
/* RaiiSupport.h */

#ifdef __cplusplus
extern "C" {
#endif

struct RaiiDesc;
typedef struct RaiiDesc* RaiiRallbackPosition;
typedef void ResourceDestructor( void* );

RaiiRallbackPosition RaiiCurrentRallbackPosition();
void RaiiRallback( RaiiRallbackPosition );
void RaiiPushResource( void* resource, ResourceDestructor* );

#ifdef __cplusplus
}
template<typename T, typename TRes>
void RaiiPush( T* resource, TRes (*dstr)( T* ) = deleteIt<T> )
{
    RaiiPushResource( resource, (ResourceDestructor*)dstr );
}

#define RAII_PUSH( resource, deallocationFunc )  \
    RaiiPush( resource, deallocationFunc );
#else
#define RAII_PUSH( resource, deallocationFunc )  \
    RaiiPushResource( resource, deallocationFunc );
#endif __cplusplus

#define RAII_ENTRY RaiiRallbackPosition raiiRallbackPosition = RaiiCurrentRallbackPosition();
#define RAII_LEAVE RaiiRallback( raiiRallbackPosition );
#define RAII_RETURN( value ) do {\
    RAII_LEAVE;\
    return (value);\
} while( 0 );

/* RaiiSupport.c*/
#include <stdlib.h>

static struct RaiiDesc {
    ResourceDestructor* Dstr;
        void* Resource;
    struct RaiiDesc prev;
} firstDesc = 0;

RaiiRallbackPosition RaiiCurrentRallbackPosition()
{
    return firstDesc;
}

void RaiiRallback( RaiiRallbackPosition stopPos )
{
    while( firstDesc != stopPos ) {
        struct RaiiDesc* cur = firstDesc;
        firstDesc = cur->prev;
        cur->Dstr( cur->Resource );
        free( cur );
    } 
}

void RaiiPushResource( void* resource, ResourceDestructor* dstr )
{
    struct RaiiDesc tmp = { dstr, resource, firstDesc );
    struct RaiiDesc* newDesc = malloc( sizeof( struct RaiiDesc ) );
    *newDesc = tmp;
    firstDesc = newDesc;
}

/* Какой-то клиентский сишник */
#inlude "RaiiSupport.h"

/*  тут дофига всего */

MySuperPuperRetType MyFuncWithRaii( /* аргументы по вкусу */ )
{
    RAII_ENTRY;
    
    xxx* data1 = malloc( ... );
    if( data1 != 0 ) {
        RAI_PUSH( data1, free );
    }

    /* тут дофига логики */
            FILE* file1 = fopen( ... );
            if( file1 != 0 ) {
                RAII_PUSH( file1, fclose );
            }
            
    /* тут ещё дофига логики */

        if( ZZZ ) {
            RAII_RETURN( SuperPuperExpression )
        }

    /* тут ещё дофига логики */
    RAII_LEAVE;
}


Писал из головы, мог накосячить. Отадку и переделку в том направении, чтобы уйти от аллокации на каждый push оставляю благодарному читателю...

Да, кстати, поверх этой хрени легко приделать и исклчения на чём-то воде лонгджампа...
Ну и вообще многие запары с владением, исключениями и прочей ерундой и в С++ разруливаются.

Скажем можно жить на каком-то отдельном аллокаторе, то, что надо таки рушить, регить в RaiiSupport'е, а в конце большого алгоритма таком или ином, звать RaiiRallback до 0 и рушить аллокатор целиком...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 17.11.11 09:58
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, sts, Вы писали:


К>Можно так ещё сделать:

К>- все ресурсы инициализируются неким сигнальным значением (NULL'ом, например)
К>- по ходу работы, выполняются захваты ресурсов, а их переменные переходят из сигнального значения в рабочее
К>- по завершении, будь то обычное или аварийное, освобождаются все ресурсы, содержащие рабочее значение

все хорошо пока ресурс — это простой тип — число или указатель.
с предопределенными структурами уже хуже получается.
Re[9]: [PURE C] Закат Солнца вручную: замена RAII ???
От: Erop Россия  
Дата: 17.11.11 10:07
Оценка: :)
Здравствуйте, sts, Вы писали:

sts>Потому нужно определить сто такое для каждой 0 и как ее выставить в 0.

sts>т.е. +2 функции к каждому типу.

В С типов нет... Есть структуры.
Скажем есть у теюя струтктура MyArray, и у неё есть поле body. Проверяешь его и всё...
Ну и вообще чё за страсть писать на С, как на С++? "pp" к расширению дописать ломает что ли?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 17.11.11 10:24
Оценка:
Здравствуйте, 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, а не реализация???
От: sts  
Дата: 17.11.11 10:33
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, sts, Вы писали:


sts>>Все способы, как кажется, — жесть какая-то


E>Обычно на С пишут немного не так, как на С++, так что получается вовсе и не жесть.

А как пишут на С ?
В этой ветке еще не все способы перечислили ?
По моим наблюдениям, в основном таки копипастят освобождение ресурсов перед каждым return.
Либо goto error_N.
Мне эти подходы не особо нравятся.

E>Но если вы жить не можете без RAII, ну заведите себе RAII!


Можно и так конечно, но хочется не RAII, а чего попроще, с Сишым подходом.

E>
/* RaiiSupport.h */

E>#ifdef __cplusplus
E>extern "C" {
E>#endif

E>struct RaiiDesc;
E>typedef struct RaiiDesc* RaiiRallbackPosition;
E>typedef void ResourceDestructor( void* );

E>RaiiRallbackPosition RaiiCurrentRallbackPosition();
E>void RaiiRallback( RaiiRallbackPosition );
E>void RaiiPushResource( void* resource, ResourceDestructor* );

E>#ifdef __cplusplus
E>}
E>template<typename T, typename TRes>
E>void RaiiPush( T* resource, TRes (*dstr)( T* ) = deleteIt<T> )
E>{
E>    RaiiPushResource( resource, (ResourceDestructor*)dstr );
E>}

E>#define RAII_PUSH( resource, deallocationFunc )  \
E>    RaiiPush( resource, deallocationFunc );
E>#else
E>#define RAII_PUSH( resource, deallocationFunc )  \
E>    RaiiPushResource( resource, deallocationFunc );
E>#endif __cplusplus

E>#define RAII_ENTRY RaiiRallbackPosition raiiRallbackPosition = RaiiCurrentRallbackPosition();
E>#define RAII_LEAVE RaiiRallback( raiiRallbackPosition );
E>#define RAII_RETURN( value ) do {\
E>    RAII_LEAVE;\
E>    return (value);\
E>} while( 0 );

E>/* RaiiSupport.c*/
E>#include <stdlib.h>

E>static struct RaiiDesc {
E>    ResourceDestructor* Dstr;
E>        void* Resource;
E>    struct RaiiDesc prev;
E>} firstDesc = 0;

E>RaiiRallbackPosition RaiiCurrentRallbackPosition()
E>{
E>    return firstDesc;
E>}

E>void RaiiRallback( RaiiRallbackPosition stopPos )
E>{
E>    while( firstDesc != stopPos ) {
E>        struct RaiiDesc* cur = firstDesc;
E>        firstDesc = cur->prev;
        cur->>Dstr( cur->Resource );
E>        free( cur );
E>    } 
E>}

E>void RaiiPushResource( void* resource, ResourceDestructor* dstr )
E>{
E>    struct RaiiDesc tmp = { dstr, resource, firstDesc );
E>    struct RaiiDesc* newDesc = malloc( sizeof( struct RaiiDesc ) );
E>    *newDesc = tmp;
E>    firstDesc = newDesc;
E>}

E>/* Какой-то клиентский сишник */
E>#inlude "RaiiSupport.h"

E>/*  тут дофига всего */

E>MySuperPuperRetType MyFuncWithRaii( /* аргументы по вкусу */ )
E>{
E>    RAII_ENTRY;
    
E>    xxx* data1 = malloc( ... );
E>    if( data1 != 0 ) {
E>        RAI_PUSH( data1, free );
E>    }

E>    /* тут дофига логики */
E>            FILE* file1 = fopen( ... );
E>            if( file1 != 0 ) {
E>                RAII_PUSH( file1, fclose );
E>            }
            
E>    /* тут ещё дофига логики */

E>        if( ZZZ ) {
E>            RAII_RETURN( SuperPuperExpression )
E>        }

E>    /* тут ещё дофига логики */
E>    RAII_LEAVE;
E>}


E>Писал из головы, мог накосячить. Отадку и переделку в том направении, чтобы уйти от аллокации на каждый push оставляю благодарному читателю...


E>Да, кстати, поверх этой хрени легко приделать и исклчения на чём-то воде лонгджампа...

E>Ну и вообще многие запары с владением, исключениями и прочей ерундой и в С++ разруливаются.

E>Скажем можно жить на каком-то отдельном аллокаторе, то, что надо таки рушить, регить в RaiiSupport'е, а в конце большого алгоритма таком или ином, звать RaiiRallback до 0 и рушить аллокатор целиком...
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: igna Россия  
Дата: 17.11.11 10:38
Оценка: +1 :)
Здравствуйте, Кодт, Вы писали:

К>А плох, соответственно, некоторым оверхедом в конце программы.


Это ведь не просто овехед, это еще и перенос логики из времени компиляции во время исполнения. Кроме того (или может быть в частности) двухстадийная инициализация ресурса не дает возможности объявить его const.
Re[3]: [PURE C] Почему замена RAII, а не реализация???
От: Erop Россия  
Дата: 17.11.11 10:42
Оценка: +1
Здравствуйте, 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[10]: [PURE C] Закат Солнца вручную: замена RAII ???
От: igna Россия  
Дата: 17.11.11 10:42
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>В С типов нет...


6.2.5 Types

. . .

Re[11]: [PURE C] Закат Солнца вручную: замена RAII ???
От: Erop Россия  
Дата: 17.11.11 10:43
Оценка: :)
Здравствуйте, igna, Вы писали:

I>

I>6.2.5 Types

I>. . .


По делу есть чего сказать?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[12]: [PURE C] Закат Солнца вручную: замена RAII ???
От: igna Россия  
Дата: 17.11.11 10:59
Оценка:
Здравствуйте, Erop, Вы писали:

E>По делу есть чего сказать?..


То есть твое "в С типов нет" было не по делу?
Re[4]: [PURE C] Почему замена RAII, а не реализация???
От: sts  
Дата: 17.11.11 11:19
Оценка:
Здравствуйте, 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, а не реализация???
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.11.11 11:28
Оценка:
Здравствуйте, sts, Вы писали:


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 при успешной инициализации, мягко говоря, не проблема (по сравнению с общей задачей)
The God is real, unless declared integer.
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: andrey.desman  
Дата: 17.11.11 13:54
Оценка: 6 (1)
Здравствуйте, sts, Вы писали:


http://rsdn.ru/forum/cpp/2904863.aspx
Автор: andrey.desman
Дата: 06.04.08
Re[13]: [PURE C] Закат Солнца вручную: замена RAII ???
От: Erop Россия  
Дата: 17.11.11 15:05
Оценка:
Здравствуйте, igna, Вы писали:

I>То есть твое "в С типов нет" было не по делу?

То есть не можешь что-то сказать по теме топика? -- не флуди...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: [PURE C] Почему замена RAII, а не реализация???
От: Erop Россия  
Дата: 17.11.11 15:13
Оценка: +1
Здравствуйте, sts, Вы писали:

sts>>>Мне эти подходы не особо нравятся.

E>>А чем goto error_N плох?

sts>как минимум при изменениях кода едет нумерация меток.

sts>да и витиевато выходит.
А! Понял. Обычно в рамках этого подхода меткам стараются давать осмысленные названия. Типа там "выход после того", "выход после сего".
А если не могут, то нумеруют, но номера никакого смысла не имеют. Номера могут идти в любом поряке...


sts>тут уже предложили ++stage с последующим switch (stage) для решения этой проблемы.

sts>но, кажется, при этом становится еще витиеватее
IMHO это ожнозначно хуже.
Тогда уж лучше завести слово битов и взводить биты, соответствующие тем переменным, которые уже построены...
А эти ++ -- это одноначно дорога в ад.

E>>>>Но если вы жить не можете без RAII, ну заведите себе RAII!


sts>кто только их не понаписал за 40-то лет.



sts>Ну, для файлов скажем это не 0, а -1.

Ну и что?
sts>Понаписали вот. Теперь мучайся
Ну флажок рядом клади...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[14]: [PURE C] Закат Солнца вручную: замена RAII ???
От: igna Россия  
Дата: 17.11.11 15:43
Оценка:
Здравствуйте, Erop, Вы писали:

E>То есть не можешь что-то сказать по теме топика? -- не флуди...


Спрашивающему-то я ответил подробно. А пройти мимо, когда говорят неправду, можно конечно, но в данном случае не нужно. Что это тебя так заело? Ну сказал глупость, ну поправили тебя, так на то и форум.
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: Alexéy Sudáchen Чили  
Дата: 18.11.11 01:54
Оценка: 9 (2)
Здравствуйте, sts, Вы писали:

sts>Всем привет.


sts>Вопрос как лучше обеспечить освобождение ресурсов в C ?

sts>Ниже на вскидку 3 способа.

foo4 — авторелиз пул

sts>Все способы, как кажется, — жесть какая-то


четвёртый на практике выглядит где-то так

YOYO_XDATA *Xtmpl_Load_Template(char *tmpl_home, char *tmpl_name)
  {
    YOYO_XDATA *doc = Xdata_Init();
    
    __Auto_Release
      {
        YOYO_BUFFER *bf;
        char *tmpl_source = Str_Join_2('/',tmpl_home,tmpl_name);
        bf = Cfile_Read_All(Cfile_Open(tmpl_source,"r"));
        Xnode_Value_Set_Str(&doc->root,"home",tmpl_home);
        Xnode_Value_Set_Str(&doc->root,"source",tmpl_source);
        Xtmpl_Macro_Template(&doc->root,bf->at,0);
      }
      
    return doc;
  }

...

YOYO_XDATA *Xtmpl_Cache_Template(char *root, char *S, char *lang)
  {
    YOYO_XDATA *doc = 0;
    YOYO_DICTO *dt = Xtmpl_Autoload_Dicto();
    void *reco = Dicto_Get(dt,S,&doc);
    if ( reco != &doc )
      doc = reco;
    else
      {
        __Try
          {
            YOYO_XNODE *n;
            doc = Xtmpl_Load_Template(root,S);
            if ( lang ) 
              {
                n = Xnode_Insert(&doc->root,"set");
                Xnode_Value_Set_Str(n,"$","#language");
                n = Xnode_Insert(n,"text");
                Xnode_Value_Set_Str(n,"$",lang);
              }
          }
        __Catch(YOYO_ERROR_DOESNT_EXIST) ;
        Dicto_Put(dt,S,__Refe(doc)); /* doc can be NULL */
      }
    return doc;
  }


Что такое __Auto_Release и __Try можно посмотреть на гитхабе
https://github.com/alexeysudachen/libyoyo/blob/master/yoyo.hc
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: Константин Л. Франция  
Дата: 20.11.11 18:03
Оценка:
Здравствуйте, Alexéy Sudáchen, Вы писали:

[]

AS>Что такое __Auto_Release и __Try можно посмотреть на гитхабе

AS>https://github.com/alexeysudachen/libyoyo/blob/master/yoyo.hc

жесть
Re: [PURE C] Закат Солнца вручную: замена RAII ???
От: Константин Л. Франция  
Дата: 20.11.11 18:06
Оценка:
Здравствуйте, sts, Вы писали:

[]

давно ничего не писал сюда.

я бы для каждой структуры написал пару foo_ctor/foo_dtor.
внутри можно четко проинициализировать, проверить и освободить.
еще вариант с goto хорош. вот только никаких stages, только именные метки.
Re[2]: [PURE C] Закат Солнца вручную: замена RAII ???
От: sts  
Дата: 21.11.11 06:15
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, sts, Вы писали:


КЛ>[]


КЛ>давно ничего не писал сюда.


КЛ>я бы для каждой структуры написал пару foo_ctor/foo_dtor.

КЛ>внутри можно четко проинициализировать, проверить и освободить.
КЛ>еще вариант с goto хорош. вот только никаких stages, только именные метки.

Прижилось разделение кода функции на 2 части — которя выделяет-удаляет ресурсы и вызывает функцию реализующую алгоритм передавая ей готовые ресурсы в параметрах. С именными метками тоже не очень получается, пока рулят флажки xxx_initialized и goto на единственную метку где эти флажки проверяются.

А вообще получается, что единый поход (кроме своего RAII, как в YOYO) выработать сложно. В каждом конкретном случае свой подход рулит.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.