Re[4]: Опять goto :)
От: McSeem2 США http://www.antigrain.com
Дата: 17.01.06 15:41
Оценка: 5 (2) +6 -1
Все, о чем ты хотел сказать, Влад, все — мимо тазика.

VD>Мой код (в продемонстрированном стиле) надежнее. И знаешь почему? Он проще воспринимается и легче отлаживается/модифицируется.


...и не работает!

Речь была именно о том, чтобы в случае облома не продолжать тупо стучать головой об стену, а аккуратно разрулить ситуацию. Аккуратно, понимаешь? Что это означает на практике? А то, что в случае, если один malloc вернул нуль, то второй раз вызвать его ты уже не имеешь права, пока что-нибудь не освободишь. Это требование так или иначе присутствовало в изначальной постановке вопроса. Ты же его успешно проигнорировал, даже не утруждая себя тем, чтобы вникнуть в суть вопроса, назвал весь код чушью, ну и вот результат — перефразируя классика, "Поздравляю Вас, гражданин семши в лужу".

Твой "надежный" код является элементарно неработоспособным. Надежно так неработоспособным. Пойми ты наконец, что кроме MS Windows и кроме памяти есть еще множество других типов ресурсов — файлы, сокеты, всяческие хэндлы. И есть язык Си, в котором все эти хэндлы приходится хэндлить вручную. Твое же сообщение эквивалентно утверждению, что "все это чушь и обрабатывать ошибки не надо вообще". В общем, оценка тебе — два с минусом.

Хорошо, представь, что у тебя есть некие функции Push и Pop. Их вызовы должны быть строго парными. При этом Push может и не сработать (вернуть ошибку). И вот в случае ошибки, единственное, что ты имеешь право сделать — это выполнить откат назад в строго обратной последовательности (иначе — BSoD). Ну и специально для тек, кто в танке. Push/Pop — это некая абстракция (так же, как и malloc/free), вместо них могут быть, например, вложенные транзакции. Да, язык — Си и другого у тебя нету.

Так что не надо начинать с оскорблений собеседника, надо начинать с того, чтобы прочитать и понять постановку задачи.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Опять goto :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.01.06 18:31
Оценка: +5 -1
Здравствуйте, ansi, Вы писали:

A>На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.


А на мой взгляд этот код весь чистейший бред. Вот как бы это написал бы я:
static dl_record_t * alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t*)calloc(sizeof(dl_record_t));

    if (!record)
        return NULL;
        
    record->url = (char*)malloc(url_len + 1);
    record->tag = (char*)malloc(tag_len + 1);
    record->last_modified = (char*)malloc(last_modified_len + 1);
    record->tmp_name = (wchar_t*)malloc((tmp_name_len + 1) * sizeof(wchar_t));

    // Если не удалось занять память хотья бы под одну структуру данных...
    if (!(record->url && record->tag && record->last_modified && record->tmp_name))
    {
        // Освобождаем все к чертям
        free(record->url); // если free() передать NULL, то она просто ничего не сделает.
        free(record->tag);
        free(record->last_modified);
        free(record);
        return NULL;    
    }

    return record;
}


Теперь поясню почему все так, а не иначе.

Дело в том, что ситуация в которой память не выделится крайне редка. Это уже исключительная ситуация. В 99% случаев память будет выделна и проблем не будт.

Так что заниматься такой мудреной обработкой ошибок, каждый раз тратя время на тучу ненужных проверок просто глупо.
Важно, чтобы код работал быстро и эффективно в тех самых 99% случаев. А вот в нешатной ситуации можно и произвести несколько лишних вызовов.
Так же важно, чтобы код связанный с основной логикой работы приложения был компактным и понятным. Вынесение проверок успешности в концец, а то и в отдельную функцию очень способствует этому.

Ну, и главное! Казалось бы... причем тут goto?
... << RSDN@Home 1.2.0 alpha rev. 628>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Опять goto :)
От: McSeem2 США http://www.antigrain.com
Дата: 18.01.06 18:59
Оценка: 1 (1) +2 :)
Здравствуйте, Anton Batenev, Вы писали:

AB>А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя? Т.е., предположим, что у нас есть два конкурирующих за память приложения, которые в процессе своей работы исчерпали всю доступную память и хотят еще ее откушать. В один из моментов, в одном из приложений, на критичном участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?


Во времена MS-DOS и написания банковского опер-дня на Borland C++, была ситуация с нехваткой памяти. И именно для объектов, которые "не создать нельзя" (там все объекты было нельзя не создать). Наилучшим решением оказалось просто выполнить exit, предварительно вызвав некий AbortTransaction без проверки ошибок (использовался Novell Netware Btrieve) и сообщив об этом на экране. Все! Никакого шаманства с постоянными проверками на NULL. Это позволило сократить объем кода чуть не в полтора раза и вписаться в доступные 400K — это был такой договор между "светлыми и темными" — админы обязаны гарантировать доступные 400K, я же не имел права превышать этот барьер. Надо ли говорить, что админы нарушали договор гораздо чаще.
Кстати, для корректного завершения приходилось резервировать пару килобайт, которые освобождались сразу же после облома.

Щас придет Влад и скажет, что я сам себе противоречу. И будет прав, потому что задачи бывают разные. В том моем случае вот такое грубоватое решение было вполне допустимо. Более того — оно было оптимальным. В других же случаях, например, где-нибудь глубоко в кишках Oracle, 100% корректная обработка ошибок — это задача первостепенной важности, а уж есть там goto или нет — не столь существенно. Я бы в таких ситуациях сделал именно с goto ибо это — наиболее очевидное, простое и надежное решение. Оператор goto сам по себе не является злом. Злом является использование goto в Фортрановском стиле, когда у нас цикл на 5 страниц и в нем goto туда-сюда, аж в глазах рябит. Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Опять goto :)
От: g_i  
Дата: 14.01.06 08:28
Оценка: 16 (3)
Здравствуйте, ansi, Вы писали:

А как-нить так не проще?


void free_record(dl_record_t** record)
{
  if (record)
  {
    free((*record)->url);
    free((*record)->tag);
    free((*record)->last_modified);
    free(*record);
    *record = 0;
  }
  return record;
}

dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = 0; //(dl_record_t *)calloc(0, sizeof(*record));

    if (
      !(record = (dl_record_t *)calloc(0, sizeof(*record))) ||
      !(record->url = (char *)calloc(0, url_len + 1))) ||
      !(record->tag = (char *)calloc(0, tag_len + 1)) ||
      !(record->last_modified = (char *)calloc(0, last_modified_len + 1)) ||
       (record->tmp_name = (wchar_t *)calloc(0, (tmp_name_len + 1) * sizeof(wchar_t))) == SYN_NULL
       )
      free_record(&record);

    return record;
}


Re[2]: Опять goto :)
От: Cyberax Марс  
Дата: 14.01.06 17:55
Оценка: +3
Владек wrote:
> Исправил ваш первый вариант (каждый этап отката не дублируется и никаких
> goto)
Ууууххх. Такое же читать невозможно. Особенно если еще пара уровней
вложенности будет.
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re: Опять goto :)
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 16.01.06 09:00
Оценка: +1 -1 :)
Здравствуйте, ansi, Вы писали:

A>На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.


Главное не считать что-то незыблемой догмой Кстати, есть статья Д. Кнута "Структурное программирование с использованием goto". PDF. Сам только просматривал С другой стороны даже в тривиальном использовании goto found вместо break что-то есть (метка содержит подсказку). Ну и следующий пример также не выглядит отталкивающим:

int i;
for(i=0; i<len; i++)
  if (A[i] == x) goto found;
not_found: 
  i = len++;
  A[i] = x;
  B[i] = 0;
found: 
  B[i]++;
Опять goto :)
От: ansi  
Дата: 14.01.06 08:04
Оценка: +1 :)
Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.

Пример с выделением памяти:
typedef struct
{
   char    *url;
   char    *tag;
   char    *last_modified;
   wchar_t *tmp_name;
} dl_record_t;


Без goto приходится писать нечто вроде:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record)
    {
        record->url = (char *)malloc(url_len + 1);

        if (!record->url)
        {
            free(record);
            record = 0;
        }
        else
        {
            record->tag = (char *)malloc(tag_len + 1);

            if (!record->tag)
            {
                free(record->url);
                free(record);
                record = 0;
            }
            else
            {
                record->last_modified = (char *)malloc(last_modified_len + 1);

                if (!record->last_modified)
                {
                    free(record->url);
                    free(record->tag);
                    free(record);
                    record = 0;
                }
                else
                {
                    record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );

                    if (record->file_name == SYN_NULL)
                    {
                        free(record->url);
                        free(record->tag);
                        free(record->last_modified);
                        free(record);
                        record = 0;
                    }
                }
            }
        }
    }

    return record;
}


Или так (но peer review это скорее всего не пройдет):
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int err_code = 0;
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record)
    {
        ++err_code; // 1
        record->url = (char *)malloc(url_len + 1);

        if (record->url)
        {
            ++err_code; // 2
            record->tag = (char *)malloc(tag_len + 1);

            if (record->tag)
            {
                ++err_code; // 3
                record->last_modified = (char *)malloc(last_modified_len + 1);

                if (record->last_modified)
                {
                    ++err_code; // 4
                    record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );

                    if (record->file_name)
                    {
                        ++err_code; // 5
                    }
                }
             }
        }
    }

    switch(err_code)
    {
        case 4: free(record->last_modified);
        case 3: free(record->tag);
        case 2: free(record->url);
        case 1: free(record);
                record = 0;
    }

    return record;
}


Или еще такой вариант:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int status = 0;
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record)
    {
        memset(record, 0, sizeof(*record));
        status = (record->url = (char *)malloc(url_len + 1)) != 0;
    }

    if (status)
    {
        status = (record->tag = (char *)malloc(tag_len + 1)) != 0;
    }

    if (status)
    {
        status = (record->last_modified = (char *)malloc(last_modified_len + 1)) != 0;
    }

    if (status)
    {
        status = (record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) )) != 0;
    }

    if (!status && record)
    {
        if (record->last_modified)
        {
            free(record->last_modified);
        }

        if (record->tag)
        {
            free(record->tag);
        }

        if (record->url)
        {
            free(record->url);
        }

        free(record);
        record = 0;
    }

    return record;
}


Иногда делают и так:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int status = 0;
    dl_record_t *record;

    while (1)
    {
        record = (dl_record_t *)malloc(sizeof(*record));
        if (!record)
        {
            break;
        }

        memset(record, 0, sizeof(*record));
        record->url = (char *)malloc(url_len + 1);
        if (!record->url)
        {
            break;
        }

        record->tag = (char *)malloc(tag_len + 1);
        if (!record->tag)
        {
            break;
        }

        record->last_modified = (char *)malloc(last_modified_len + 1);
        if (!record->last_modified)
        {
            break;
        }

        record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );
        if (!record->tmp_name)
        {
            break;
        }

        status = 1;
        break;
    }

    if (!status && record)
    {
        if (record->last_modified)
        {
            free(record->last_modified);
        }

        if (record->tag)
        {
            free(record->tag);
        }

        if (record->url)
        {
            free(record->url);
        }

        free(record);
        record = 0;
    }

    return record;
}


А вот так выглядит вариант с goto:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (!record)
    {
        return 0;
    }

    if (!(record->url = (char *)malloc(url_len + 1)))
    {
        goto fail_record_url;
    }

    if (!(record->tag = (char *)malloc(tag_len + 1)))
    {
        goto fail_tag;
    }

    if (!(record->last_modified = (char *)malloc(last_modified_len + 1)))
    {
        goto fail_last_modified;
    }

    if (!(record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) )))
    {
        goto fail_tmp_name;
    }

    return record;

    fail_tmp_name:      free(record->last_modified);
    fail_last_modified: free(record->tag);
    fail_tag:           free(record->url);
    fail_record_url:    free(record);

    return 0;
}


На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.

Но все дело в том, что лично мне писать приходится на C. Какие еще могут быть варианты проведения отката?

25.01.06 07:41: Перенесено модератором из 'Философия программирования' — VladD2
Re: Опять goto :)
От: Vovka_R Россия  
Дата: 14.01.06 19:27
Оценка: :))
Здравствуйте, ansi, Вы писали:

А если так:

static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int cnt = 0;
    dl_record_t *record;

    while (1)
    {
        if ( !(record = (dl_record_t *)malloc(sizeof(*record))) )
        {
            cnt = 1;
            break;
        }

        memset(record, 0, sizeof(*record));
        
        if ( !(record->url = (char *)malloc(url_len + 1)) )
        {
            cnt = 2;
            break;
        }
        
        if ( !(record->tag = (char *)malloc(tag_len + 1)) )
        {
            cnt = 3;
            break;
        }
        
        if ( !(record->last_modified = (char *)malloc(last_modified_len + 1)) )
        {
            cnt = 4;
            break;
        }
        
        if ( !(record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) )) )
        {
            cnt = 5;
            break;
        }

        break;
    }

    switch(cnt)
    {
        case 5: free(record->last_modified);
        case 4: free(record->tag);
        case 3: free(record->url);
        case 2: free(record); record = NULL;
    }

    return record;
}
Re: Опять goto :)
От: alexeiz  
Дата: 14.01.06 21:18
Оценка: +1 :)
A> dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

Замашки у вас С++сные, товарищи Сишники.
Re[6]: Опять goto :)
От: Anton Batenev Россия https://github.com/abbat
Дата: 19.01.06 06:18
Оценка: +2
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

MS>>... Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно.

LCR>Извини, это ты про какие сравнения? Примерчик можно?

Вероятно,

double i = 0.0;
if (i != 0.0)
    ; // always false


Т.е. имется ввиду случай равенства, когда одинаковая по величине погрешность присутствует в обоих частях сравниваемого выражения, что и дает равенство вне зависимости от величины погрешности.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Опять goto :)
От: Владек Россия Github
Дата: 14.01.06 15:11
Оценка: 15 (1)
Здравствуйте, ansi, Вы писали:

A>Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.


A>Пример с выделением памяти:

A>
A>typedef struct
A>{
A>   char    *url;
A>   char    *tag;
A>   char    *last_modified;
A>   wchar_t *tmp_name;
A>} dl_record_t;
A>


A>Но все дело в том, что лично мне писать приходится на C. Какие еще могут быть варианты проведения отката?


Исправил ваш первый вариант (каждый этап отката не дублируется и никаких goto)
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record != NULL)
    {
        record->url = (char *)malloc(url_len + 1);

        if (record->url != NULL)
        {
            record->tag = (char *)malloc(tag_len + 1);

            if (record->tag != NULL)
            {
                record->last_modified = (char *)malloc(last_modified_len + 1);

                if (record->last_modified != NULL)
                {
                    record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );

                    if (record->tmp_name != SYN_NULL)
                    {
                        return record;
                    }
                    free(record->last_modified);
                }
                free(record->tag);
            }
            free(record->url);
        }
        free(record);
    }

    return NULL;
}


Кстати, пишите на С, а проверки NULL-checks используете из C++! Нехорошо, товарищ!
... << RSDN@Home 1.1.4 stable rev. 510>>
Re: Опять goto :)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 15.01.06 15:36
Оценка: 3 (1)
Здравствуйте, ansi, Вы писали:

A>Пример с выделением памяти:

A>
A>typedef struct
A>{
A>   char    *url;
A>   char    *tag;
A>   char    *last_modified;
A>   wchar_t *tmp_name;
A>} dl_record_t;
A>


Может уже предлагали такой вариант, но все же:
static dl_record_t *
destroy_record( dl_record_t * r )
  {
    if( NULL != r->url )
      free( r->url );
    if( NULL != r->tag )
      free( r->tag );
    if( NULL != r->last_modified )
      free( r->last_modified );
    if( NULL != r->tmp_name )
      free( r->tmp_name );
    free( r );
    return 0;
  }

static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
  {
    dl_record_t * record = (dl_record_t *) malloc( sizeof( dl_record_t ) );
    if( record )
      {
        record->url_len = NULL;
        record->tag = NULL;
        record->last_modified = NULL;
        record->tmp_name = NULL;

        if( NULL == ( record->url_len = (char *)malloc( url_len + 1 ) ) )
          return destroy_record( record );
        if( NULL == ( record->tag = (char *)malloc( tag_len + 1 ) ) )
          return destroy_record( record );
        if( NULL == ( record->last_modified = (char *)malloc( last_modified_len + 1 ) ) )
          return destroy_record( record );
        if( NULL == ( record->tmp_name = (wchar_t *)malloc( sizeof( wchar_t ) * ( tmp_name_len + 1 ) ) ) )
          return destroy_record( record );
      }

    return record;
  }


A>А вот так выглядит вариант с goto:

A>
A>static dl_record_t *
A>alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
A>{
A>    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

...

A>    fail_tmp_name:      free(record->last_modified);
A>    fail_last_modified: free(record->tag);
A>    fail_tag:           free(record->url);
A>    fail_record_url:    free(record);

A>    return 0;
A>}
A>


Этот вариант мне не нравится тем, что две не сильно связанные по логике части alloc_record (аллокация и деаллокация) оказываются сильно связанны порядком действий. Стоит только кому-то переместить выделение памяти для tmp_name в начало секции аллокации и оставить неизменной секцию деаллокации, как весь механизм рушится. А такое перемещение вполне возможно. Например, если мы знаем, что больше всего памяти требуется именно для tmp_name, то лучше попробовать выделить эту память вначале и, только если это удалось, выделять память под остальные поля.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Опять goto :)
От: c-smile Канада http://terrainformatica.com
Дата: 15.01.06 06:43
Оценка: 2 (1)
Здравствуйте, ansi, Вы писали:

A>Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.


A>Пример с выделением памяти:


Если уже на то по пошло то на C это пишется так:

typedef struct
{
   char    *url;
   char    *tag;
   char    *last_modified;
   wchar_t *tmp_name;
} dl_record_t;


static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(dl_record_t) + 
      url_len + 1 + tag_len + 1 + last_modified_len + 1+ (tmp_name_len + 1) * sizeof(wchar_t);

    if(record)
    {
      record->url = ...;
      record->tag = ...;
      record->last_modified = ...;
      record->tmp_name =...;
    }

 );
 return record;
}


А еще есть такие штуки как memory pool из APR.
Re: Опять goto :)
От: Zdreni Украина http://r7.org.ru
Дата: 14.01.06 08:35
Оценка: :)
Здравствуйте, ansi, Вы писали:

A>Но все дело в том, что лично мне писать приходится на C. Какие еще могут быть варианты проведения отката?


Единственный нормальный, IMHO — каркас обработки ошибок и освобождения ресурсов, реализованный при помощи setjmp()/longjmp().
Re[2]: Опять goto :)
От: Quintanar Россия  
Дата: 14.01.06 10:46
Оценка: +1
Здравствуйте, g_i, Вы писали:

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


g_i>А как-нить так не проще?



Нет, потому что у тебя здесь сразу два допущения.
1) То, что ресурсы можно безопасно освободить, не зная были ли они получены
2) Что выделения ресурсов идут подряд и их можно впехнуть в if

Так не бывает (с)

В С goto для таких случаев рулит, с этим ничего не поделаешь.
Re[4]: Опять goto :)
От: _Winnie Россия C++.freerun
Дата: 16.01.06 18:45
Оценка: +1
Здравствуйте, VladD2, Вы писали:


VD>Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер. Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конец деструкторы С++ и т.п.


Ы-гхмы-кхы-кхм-гы-уг!!!

public class DisposeExample
{
    // A base class that implements IDisposable.
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources.
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource.
        private IntPtr handle;
        // Other managed resource this class uses.
        private Component component = new Component();
        // Track whether Dispose has been called.
        private bool disposed = false;

        // The class constructor.
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue 
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed.
        private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources.
                if(disposing)
                {
                // Dispose managed resources.
                component.Dispose();
                }
             
                // Call the appropriate methods to clean up 
                // unmanaged resources here.
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;            
            }
            disposed = true;         
        }

        // Use interop to call the method necessary  
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code.
        // This destructor will run only if the Dispose method 
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide destructors in types derived from this class.
        ~MyResource()      
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create
        // and use the MyResource object.   
    }
}


(c) MSDN
И Саттер с Рихером говорят, что тут не всё так просто и тривиально.
Правильно работающая программа — просто частный случай Undefined Behavior
Re[6]: Опять goto :)
От: _Winnie Россия C++.freerun
Дата: 17.01.06 12:05
Оценка: +1
Здравствуйте, VladD2, Вы писали:

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



_W>>Ы-гхмы-кхы-кхм-гы-уг!!!


VD>Ну, и нафига ты тут этот диспоз привел?

VD>Зачем он для управления памятью?



Ладно...
Вообще-то сначала было сказано —
VD>>>Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер.
Потом, правда, добавлено —
VD>>>Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конец деструкторы С++ и т.п.

Кроме памяти есть и другие ресурсы.
Ok.
Память — так память.

А shared memory между процессами — не память?
А аллоцированная видеопамять — не память (их десяток видов — вершинные буфера, вертексные, разные виды текстур, шейдеры и тд)?
А аллоцированные буферы в звуковой карте — не память?
А выделенная внутри сишной библиотеки память по malloc — не память? (Мы handle на какой-то объект, совсем не на память, но память принадлежит ему и мы освобождаем её косвенно)
А память на магнитных дисках (временные файлы) — не память?
А флеш-память (временные файлы там же) — не память?
А память на стороне базы данных на сервере для обработки моего запроса — не память?

И самое главное.
А память программиста — разве не память?
Почему я должен держать в голове столько тонкостей реализации dispose-паттерна?
Правильно работающая программа — просто частный случай Undefined Behavior
Re[3]: Опять goto :)
От: c-smile Канада http://terrainformatica.com
Дата: 19.01.06 01:53
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, c-smile, Вы писали:



CS>>Если уже на то по пошло то на C это пишется так:...


VD>А если в последсвтии нужно будет перезанять пмять под одно из полей?


Для этого в структуру еще вводится одно поле — allocatedSize.
Этого хвататет чтобы потом правильно её удалить и те поля которые
переаллоцированы т.е. указывают за пределы structPtr + allocatedSize;
Re[5]: Опять goto :)
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 19.01.06 05:19
Оценка: :)
McSeem2,

MS>... Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно.


Извини, это ты про какие сравнения? Примерчик можно?
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[3]: Опять goto :)
От: MShura  
Дата: 14.01.06 11:05
Оценка:
Q>Нет, потому что у тебя здесь сразу два допущения.
Единственное допущение это то, что NULL представлятся в виде (void*)0
Во всем остальном функции логически идентичены.


Q>В С goto для таких случаев рулит, с этим ничего не поделаешь.

Он рулит, но не для таких случаев.
Re[3]: Опять goto :)
От: g_i  
Дата: 14.01.06 11:39
Оценка:
Здравствуйте, Quintanar, Вы писали:


Q>Нет, потому что у тебя здесь сразу два допущения.

Q>1) То, что ресурсы можно безопасно освободить, не зная были ли они получены
Q>2) Что выделения ресурсов идут подряд и их можно впехнуть в if

Q>Так не бывает (с)


Q>В С goto для таких случаев рулит, с этим ничего не поделаешь.


Из кода, приведенного в примере следуют эти самые допущения.
Согласен, что в общем случае все не так просто. А частный, наверное, не интересен.
Re[3]: Опять goto :)
От: Владек Россия Github
Дата: 14.01.06 18:13
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Владек wrote:

>> Исправил ваш первый вариант (каждый этап отката не дублируется и никаких
>> goto)
C>Ууууххх. Такое же читать невозможно. Особенно если еще пара уровней
C>вложенности будет.

Ну тогда обнулять структуру перед аллокацией полей и чуть-что сразу вызывать dealloc_всех_полей_и_структуры() и return NULL. Не нравится мне это дублирование вызовов просто.
Игра в городки развивает силу рук и меткость глаз. (С) Годзилла
Re[2]: Опять goto :)
От: ansi  
Дата: 15.01.06 10:34
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Если уже на то по пошло то на C это пишется так:


CS>
CS>static dl_record_t *
CS>alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
CS>{
CS>    dl_record_t *record = (dl_record_t *)malloc(sizeof(dl_record_t) + 
CS>      url_len + 1 + tag_len + 1 + last_modified_len + 1+ (tmp_name_len + 1) * sizeof(wchar_t);

CS>    if(record)
CS>    {
CS>      record->url = ...;
CS>      record->tag = ...;
CS>      record->last_modified = ...;
CS>      record->tmp_name =...;
CS>    }

CS> );
CS> return record;
CS>}
CS>


Ты знаешь, именно так я и написал сначала Но! Проц не интеловский (арм, по-моему). Чтение 16 бит по нечетному адресу...и падает весь телефон Возиться с выравниванием не хотелось. Да и вообще, память там разбита на блоки фиксированного размера (начиная с кучи блоков по 4байт, ..., и заканчивая парой блоков по 64Кб), причем блоки не объединяются. Так что вероятность не получить один большой блок больше, чем вероятность не получить несколько блоков размером поменьше.

CS>А еще есть такие штуки как memory pool из APR.
Re[2]: Опять goto :)
От: ansi  
Дата: 15.01.06 10:52
Оценка:
Здравствуйте, VladD2, Вы писали:

A>>На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.


VD>А на мой взгляд этот код весь чистейший бред. Вот как бы это написал бы я:

VD>
VD>static dl_record_t * alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
VD>{
VD>    dl_record_t *record = (dl_record_t*)calloc(sizeof(dl_record_t));

VD>    if (!record)
VD>        return NULL;
        
VD>    record->url = (char*)malloc(url_len + 1);
VD>    record->tag = (char*)malloc(tag_len + 1);
VD>    record->last_modified = (char*)malloc(last_modified_len + 1);
VD>    record->tmp_name = (wchar_t*)malloc((tmp_name_len + 1) * sizeof(wchar_t));

VD>    // Если не удалось занять память хотья бы под одну структуру данных...
VD>    if (!(record->url && record->tag && record->last_modified && record->tmp_name))
VD>    {
VD>        // Освобождаем все к чертям
VD>        free(record->url); // если free() передать NULL, то она просто ничего не сделает.
VD>        free(record->tag);
VD>        free(record->last_modified);
VD>        free(record);
VD>        return NULL;    
VD>    }

VD>    return record;
VD>}
VD>


А на мой взгляд, код q_i все-же лучше. Идея та же, но при падении одного вызова, все остальные просто не выполняются. Ты же идешь до последнего

VD>Теперь поясню почему все так, а не иначе.


VD>Дело в том, что ситуация в которой память не выделится крайне редка. Это уже исключительная ситуация. В 99% случаев память будет выделна и проблем не будт.


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

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

VD>Важно, чтобы код работал быстро и эффективно в тех самых 99% случаев. А вот в нешатной ситуации можно и произвести несколько лишних вызовов.

VD>Так же важно, чтобы код связанный с основной логикой работы приложения был компактным и понятным. Вынесение проверок успешности в концец, а то и в отдельную функцию очень способствует этому.
Вот серьезно, есть ли проблемы с пониманием варианта с goto?

Да, в принципе согласен и склоняюсь к этому варианту, потому как других более разумных просто нет — goto находится под строжайшим запретом! Не дай Бог выставить код с goto на инспекцию — инфаркт инспекторам обеспечен

VD>Ну, и главное! Казалось бы... причем тут goto?
Re[2]: Опять goto :)
От: ansi  
Дата: 15.01.06 11:00
Оценка:
Здравствуйте, g_i, Вы писали:

g_i>А как-нить так не проще?


Да, вариант неплохой! Но, у меня calloc в распоряжении нет, да и malloc'а тоже. Менеджер свой. Можно конечно и написать нечто вроде calloc, но уже не то (с). Поэтому аллоцирование структуры придется вынести из if'а и добавить memset. К счастью, аналог функции free освобождает нулевой указатель без проблем. Но это не со всеми ресурсами.
Замечу: вариант с goto не требует зануления структуры и проверок на выделенность ресурсов.
Re[3]: Опять goto :)
От: Anton Batenev Россия https://github.com/abbat
Дата: 15.01.06 11:36
Оценка:
Здравствуйте, ansi, Вы писали:

A>Согласен, но проверки в любом случае нужны. Надежность — основное требование. Потому как если что-то где-то падает, то оно тянет за собой абсолютно все.


А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя? Т.е., предположим, что у нас есть два конкурирующих за память приложения, которые в процессе своей работы исчерпали всю доступную память и хотят еще ее откушать. В один из моментов, в одном из приложений, на критичном участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Опять goto :)
От: ansi  
Дата: 15.01.06 13:19
Оценка:
Здравствуйте, Anton Batenev, Вы писали:

AB>А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя? Т.е., предположим, что у нас есть два конкурирующих за память приложения, которые в процессе своей работы исчерпали всю доступную память и хотят еще ее откушать. В один из моментов, в одном из приложений, на критичном участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?


Очевидно, что откат, но только чтоб никто не заметил Такие ситуации довольно редки, на мой взгляд. Если отказ и будет получен, то скорее всего работоспособность можно сохранить, потому как нехватает ресурсов только для какой-то части функционала, а ронять все не надо. Но если работоспособность сохранить невозможно, то придется аккуратно (или с треском — кому как нравится) вывалиться — другого выхода просто нет...
Re[4]: Опять goto :)
От: Cyberax Марс  
Дата: 15.01.06 13:59
Оценка:
Anton Batenev wrote:
> А вот меня всегда интересовал вопрос, что делать, если вызов malloc
> вернул NULL для объекта, который *не* создать нельзя? Т.е., предположим,
> что у нас есть два конкурирующих за память приложения, которые в
> процессе своей работы исчерпали всю доступную память и хотят еще ее
> откушать. В один из моментов, в одном из приложений, на критичном
> участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?
Падать.

Как вариант — пытаться освободить кэши/временные данные и снова
повторить вызов.
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re[5]: Опять goto :)
От: Anton Batenev Россия https://github.com/abbat
Дата: 15.01.06 18:21
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Падать.

C>Как вариант — пытаться освободить кэши/временные данные и снова
C>повторить вызов.

Ну если предположить, что все освобождается тогда, когда более не требуется и, как следствие, к моменту Х освобождать нечего, а откатываться тоже некуда, то получаем интересный парадокс — на критичных участках кода проверки на корректность выделения памяти теряют смысл — ибо все равно падаем.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Опять goto :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.01.06 19:09
Оценка:
Здравствуйте, ansi, Вы писали:

A>А на мой взгляд, код q_i все-же лучше. Идея та же, но при падении одного вызова, все остальные просто не выполняются. Ты же идешь до последнего


...

A>Согласен, но проверки в любом случае нужны. Надежность — основное требование. Потому как если что-то где-то падает, то оно тянет за собой абсолютно все.


Мой код (в продемонстрированном стиле) надежнее. И знаешь почему? Он проще воспринимается и легче отлаживается/модифицируется.

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

A>Вот серьезно, есть ли проблемы с пониманием варианта с goto?


Еще какие. Это заставляет программиста решать шарады, а не просто проанализировать код и пойти дальше.

ЗЫ

Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер. Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конце деструкторы С++ и т.п.
... << RSDN@Home 1.2.0 alpha rev. 628>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Опять goto :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.01.06 00:12
Оценка:
Здравствуйте, _Winnie, Вы писали:

VD>>Да... Нормальное решение подобных задачь — это переложить отвественность за управление ресурсами на компьютер. Самый лучший способ автоматическое управление памятью (ЖЦ и т.п.). На худой конец деструкторы С++ и т.п.


_W>Ы-гхмы-кхы-кхм-гы-уг!!!


Ну, и нафига ты тут этот диспоз привел?
Зачем он для управления памятью?
... << RSDN@Home 1.2.0 alpha rev. 628>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Опять goto :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.01.06 00:12
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>Если уже на то по пошло то на C это пишется так:...


А если в последсвтии нужно будет перезанять пмять под одно из полей?
... << RSDN@Home 1.2.0 alpha rev. 628>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Опять goto :)
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 17.01.06 12:17
Оценка:
Здравствуйте, Anton Batenev, Вы писали:

AB>А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя?


Такие объекты можно создавать в стеке, распределять под них память статически, либо иметь для них специальный буфер с запасом. Обычно таких объектов не много. В целом наблюдается примерно такая ситуация: рост памяти редко происходит сам по себе, а обычно связан с выполнением некоторой операции. Есть для выполнения операции памяти недостаточно, то операция прерывается и мы возвращаемся в то состояние, которое предшествовало выполнению операции.
Re[7]: Опять goto :)
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 19.01.06 09:31
Оценка:
Anton Batenev,

AB>Вероятно,

AB>
AB>double i = 0.0;
AB>if (i != 0.0)
AB>    ; // always false
AB>


Да, очевидно Максим имел ввиду это.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[7]: Опять goto :)
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 19.01.06 10:23
Оценка:
_Winnie,

_W>И самое главное.

_W>А память программиста — разве не память?
_W>Почему я должен держать в голове столько тонкостей реализации dispose-паттерна?

Каркас этого, с позволения сказать, "паттерна":
Dispose(bool) -> // удаление в зависимости от того, откуда вызов
Dispose() содержит Dispose(true), вызывается явно
~MyReso() содержит Dispose(false), вызывается неявно

Остальные детали достраиваются автоматически (флаг там, доступ к полям и неупр. ресурсам).

Как ты понимаешь, я ещё жив, и переполнения памяти не произошло.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[5]: Опять goto :)
От: shadone Норвегия  
Дата: 19.01.06 20:50
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS> Это примерно так же, как молодых учат не сравнивать double на равенство, типа этого делать нельзя. На самом деле вполне можно и даже нужно — надо только понимать, когда это можно и нужно.


хм, а когда можно и нужно сравнивать double ?
Re[6]: Опять goto :)
От: McSeem2 США http://www.antigrain.com
Дата: 19.01.06 23:32
Оценка:
Здравствуйте, shadone, Вы писали:

S>хм, а когда можно и нужно сравнивать double ?


Ну, например, в алгоритме General Polygon Clipper by Alan Murta http://www.cs.man.ac.uk/~toby/alan/software/
Более того, там важна даже разница между меньше и меньше-равно. А случай примерно такой:
double a = 100;
double b = a;
if(a == b) . . .


Но понятно, что два теоретически одинаковые значения, но вычисленные по-разному (один через синус, другой через экспоненту) надо сравнивать с некой epsilon.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Опять goto :)
От: ecco Россия  
Дата: 25.01.06 09:04
Оценка:
Здравствуйте, ansi, Вы писали:

A>Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.


А не судьба сделать так?

static dl_record_t* alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    unsigned nSize = sizeof(dl_record_t) + ( url_len + 1 ) + ( tag_len + 1 ) +
                                            ( last_modified_len + 1 ) + ( ( tmp_name_len + 1 ) * sizeof(wchar_t) );
    
        char* pMem = (char*)malloc( nSize );
        if ( !pMem )
        {
                return NULL;
        }
        
        dl_record_t *record = (dl_record_t *)pMem;        pMem += sizeof(dl_record_t);
        
        record->url = pMem;                                                        pMem += url_len + 1;
        record->tag = pMem;                                                        pMem += tag_len + 1;
        record->last_modified = pMem;                                    pMem += last_modified_len + 1;
        record->tmp_name = (wchar_t *)pMem;
}


Пойдёт?
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.