Опять 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
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.