Какой прототип выбрать?
От: DirtyGarry  
Дата: 19.10.11 05:54
Оценка:
Есть блок данных. В этом блоке нужно найти данные и вернуть указатель на них. Но иногда данных в самом блоке нет, а есть лишь описание (размер, откуда прочитать и т.п.). Родился следующий (условный) прототип функции

BYTE* get_need_data(const BYTE* source_data, DATA_TYPE type, BYTE* need_data_buf)

— source_data [in] — исходные данные
— type [in] — "критерий" поиска нужных данных
— need_data_buf [out] — буфер, содержащий нужные данные, если они не в source_data

Функция всегда возвращает указатель на нужные данные. Если они находятся внутри source_data, то она вернет указатель, которые указывает внутрь source_data. Если нужных данных внутри source_data нет, она сама выделяет буфер need_data_buf, копирует туда данные и возвращает указатель на этот буфер.

Является ли такой подход естественным? Или, например, лучше разделить эту функцию на две: одна находит данные внутри source_data, а другая читает из вне?

Спасибо.
Re: Какой прототип выбрать?
От: alexeiz  
Дата: 20.10.11 02:25
Оценка: +1
Здравствуйте, DirtyGarry, Вы писали:

DG>Есть блок данных. В этом блоке нужно найти данные и вернуть указатель на них. Но иногда данных в самом блоке нет, а есть лишь описание (размер, откуда прочитать и т.п.). Родился следующий (условный) прототип функции


DG>BYTE* get_need_data(const BYTE* source_data, DATA_TYPE type, BYTE* need_data_buf)


DG>- source_data [in] — исходные данные

DG>- type [in] — "критерий" поиска нужных данных
DG>- need_data_buf [out] — буфер, содержащий нужные данные, если они не в source_data

DG>Функция всегда возвращает указатель на нужные данные. Если они находятся внутри source_data, то она вернет указатель, которые указывает внутрь source_data. Если нужных данных внутри source_data нет, она сама выделяет буфер need_data_buf, копирует туда данные и возвращает указатель на этот буфер.


DG>Является ли такой подход естественным? Или, например, лучше разделить эту функцию на две: одна находит данные внутри source_data, а другая читает из вне?


Посмотри, как ты будешь работать с такой функцией:
byte * allocated_data = 0;
byte const * needed_data = get_need_data(source, type, &allocated_data);

// process needed_data...

delete [] allocated_data;

Получается не очень красиво. Работаешь с одними данными, а освобождаешь другие. Связь между ними не ясна.

Тут лучше будет возвращать из функции объект, который сам знает, как себя освободить правильно. Например, хорошо подойдет shared_ptr с нужным deleter.
void array_deleter(byte const * arr)
{
    delete [] arr;
}

void null_deleter(byte const *) {}

shared_ptr<byte const> get_data(byte const * source, data_type type)
{
    if (byte const * found = find_data(source, type))  // нашли?
        return shared_ptr<byte const>(found, null_deleter);

    // иначе создадим
    byte * created = new byte[data_size]; 

    // сразу же отдадим во владение к shared_ptr
    shared_ptr<byte const> result(created, array_deleter);

    // теперь можно заполнять created...

    return result;
}
Re[2]: Какой прототип выбрать?
От: Аноним  
Дата: 20.10.11 05:05
Оценка:
Здравствуйте, alexeiz, Вы писали:

A>Получается не очень красиво. Работаешь с одними данными, а освобождаешь другие. Связь между ними не ясна.


Задумаюсь, спасибо.

A>Тут лучше будет возвращать из функции объект, который сам знает, как себя освободить правильно. Например, хорошо подойдет shared_ptr с нужным deleter.


Нужен чистый C.
Re: Какой прототип выбрать?
От: ra88  
Дата: 20.10.11 12:38
Оценка:
Здравствуйте, DirtyGarry, Вы писали:

DG>BYTE* get_need_data(const BYTE* source_data, DATA_TYPE type, BYTE* need_data_buf)


DG>- source_data [in] — исходные данные

DG>- type [in] — "критерий" поиска нужных данных
DG>- need_data_buf [out] — буфер, содержащий нужные данные, если они не в source_data

DG>Функция всегда возвращает указатель на нужные данные. Если они находятся внутри source_data, то она вернет указатель, которые указывает внутрь source_data. Если нужных данных внутри source_data нет, она сама выделяет буфер need_data_buf, копирует туда данные и возвращает указатель на этот буфер.


Если данные найдутся, то вы вернёте указатель на const BYTE как указатель на BYTE. Это замечательный хак. У меня gcc только warning выдал, и после этого можно портить const данные.
Если не найдутся, то вы выделяете память внутри функции. Кто её будет освобождать? как вы узнаете выделилась новая память или вернулась старая?

Исходя из контекста вашей задачи, надо переосмыслить поведение функции.
while true;
Re[2]: Какой прототип выбрать?
От: DirtyGarry  
Дата: 21.10.11 06:32
Оценка:
Здравствуйте, ra88, Вы писали:

R>Если данные найдутся, то вы вернёте указатель на const BYTE как указатель на BYTE. Это замечательный хак. У меня gcc только warning выдал, и после этого можно портить const данные.

R>Если не найдутся, то вы выделяете память внутри функции. Кто её будет освобождать? как вы узнаете выделилась новая память или вернулась старая?

Вы правы, я поторопился, когда писал Правильно вот так

[ccode]
const BYTE* get_need_data(const BYTE* source_data, DATA_TYPE type, BYTE** need_data_buf);
[ccode]

Память буду освобождать, если need_data_buf будет не NULL.
Re: Какой прототип выбрать?
От: pronvit  
Дата: 21.10.11 18:22
Оценка:
Здравствуйте, DirtyGarry, Вы писали:

DG>Есть блок данных. В этом блоке нужно найти данные и вернуть указатель на них. Но иногда данных в самом блоке нет, а есть лишь описание (размер, откуда прочитать и т.п.). Родился следующий (условный) прототип функции


DG>BYTE* get_need_data(const BYTE* source_data, DATA_TYPE type, BYTE* need_data_buf)


замените последний параметр на bool *allocated. если его поставила функция в 1, то надо освободить возвращённый указатель, если нет, то не надо. проверка флага как-то более естественна имхо.
Re: Какой прототип выбрать?
От: okman Беларусь https://searchinform.ru/
Дата: 21.10.11 20:18
Оценка: 2 (1)
Здравствуйте, DirtyGarry, Вы писали:

DG>BYTE* get_need_data(const BYTE* source_data, DATA_TYPE type, BYTE* need_data_buf)


DG>Функция всегда возвращает указатель на нужные данные. Если они находятся внутри source_data, то она вернет указатель, которые указывает внутрь source_data. Если нужных данных внутри source_data нет, она сама выделяет буфер need_data_buf, копирует туда данные и возвращает указатель на этот буфер.


DG>Является ли такой подход естественным? Или, например, лучше разделить эту функцию на две: одна находит данные внутри source_data, а другая читает из вне?


Естественным — не уверен, но удобным точно нет.
Клиентский код, выходит, должен проверять не только возвращенное значение, но и
последний параметр. А во втором случае еще и очищать выделенную память.

Если фантазировать в терминах чистого C, я бы переписал этот код с использованием
непрозрачных хэндлов:

// Declare handle.
typedef struct _DATA_BUFFER
{
    BYTE    * Ptr;
    BOOLEAN   fAllocated;
    // ...
} DATA_BUFFER, *PDATA_BUFFER;

typedef PDATA_BUFFER SOME_HANDLE;

SOME_HANDLE get_data(BYTE const *pSourceData, DATA_TYPE Type);
BYTE *get_buffer(SOME_HANDLE Handle);
VOID release_buffer(SOME_HANDLE Handle);

Весь код поиска данных, включая вариант с выделением буфера, прячется внутри get_data.
Наружу клиент получает только SOME_HANDLE, который на самом деле не более чем указатель
на экземпляр DATA_BUFFER. Из этого хэндла извлекается адрес (get_buffer) и, возможно,
другие атрибуты, полученные в результате поиска. Когда работа с буфером окончена, хэндл
утилизируется при помощи функции release_buffer, а она уже самостоятельно решает, надо
ли освобождать выделенный участок памяти или нет (fAllocated), освобождая от этой
заботы клиентский код. Да, знаю — это все немного нудно, зато более безопасно.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.