Всем привет. Мучаюсь исключительно женским вопросом
Есть такой класс
template<bla-bla>
class MemChunk
{
void *mem;
unsigned int nWrittenBytes;
...
unsigned int write(const void *data, unsigned int dataSize);
}
Иногда возникает необходимость отдать mem наружу — например, его может домагиваться fread(). Задача — сохранить состояние валидным(т.е nWrittenBytes поправить в нужном количестве).
Какие вижу варианты:
1. Ничего никому не давать. Ибо нефиг. Минусы — оверхед на дополнительном выделении памяти под буфер и его копирование внутрь.
2. Давать, и молиться, чтобы не забыли также потом позвать setDataSize(или adjust, не суть).
3. Давать, но чтоб женился — через функтор(функцию?), который вернёт использованный размер. Многих пугает организация подобных мероприятий.
4. Подсказка из зала.
Здравствуйте, Ухты, Вы писали:
У>Всем привет. Мучаюсь исключительно женским вопросом
В таком случае ответы наверняка будут мужскими =)
У>Есть такой класс
У>У>template<bla-bla>
У>class MemChunk
У>{
У> void *mem;
У> unsigned int nWrittenBytes;
У>...
У> unsigned int write(const void *data, unsigned int dataSize);
У>}
У>
У>Иногда возникает необходимость отдать mem наружу — например, его может домагиваться fread(). Задача — сохранить состояние валидным(т.е nWrittenBytes поправить в нужном количестве).
...
У>3. Давать, но чтоб женился — через функтор(функцию?), который вернёт использованный размер. Многих пугает организация подобных мероприятий.
Вот этот вариант мне импонирует больше всех. + такая функция(м.б. интерфейс(*) должна сама заботится о выполнении инвариантов. Т.е. если что-то нужно подправить, то она и должна это выполнять.
(*) Интерфейс "вообще" (не обязательно с виртуальными функциями)
Здравствуйте, Ухты, Вы писали:
У>1. Ничего никому не давать. Ибо нефиг. Минусы — оверхед на дополнительном выделении памяти под буфер и его копирование внутрь.
У>2. Давать, и молиться, чтобы не забыли также потом позвать setDataSize(или adjust, не суть).
У>3. Давать, но чтоб женился — через функтор(функцию?), который вернёт использованный размер. Многих пугает организация подобных мероприятий.
У>4. Подсказка из зала.
Сделать пару методов get_buffer(size1) / release_buffer(size2), size2 <= size1
И уже поверх этих примитивов делать scope guard, который финализировал бы резервирование буфера.
template<blablabla>
class mem_chunk
{
.....
void* get_buffer(size_t required);
void release_buffer(size_t written);
void release_whole_buffer(); // written = required
};
struct nodelete
{
void operator()(void*) {}
};
template<blablabla>
class mem_chunk_buffer_t
{
typeef mem_chunk<blablabla> the_chunk;
unique_ptr<the_chunk, nodelete> host; // эстафетное упоминание (чтобы промежуточные объекты не пытались финализировать повторно)
mem_chunk_buffer_t(the_chunk* host, size_t required) : host(host) { host->get_buffer(required); }
~mem_chunk_buffer_t() { if(host) host->release_whole_buffer(); }
};
template<blablabla>
mem_chunk_buffer_t<blablabla>
mem_chunk_buffer(mem_chunk<blablabla>& chunk, size_t size)
{
return mem_chunk_buffer_t<blablabla>(&chunk, size);
}
......
mem_chunk<xxxxx> mc;
fread(mem_chunk_buffer(mc, 123), 1, 123, fp);
assert(mc.nWrittenBytes == 123);
(Навеяно ATL CStringT + CStrBufT)
Можно, конечно, поизвращаться — чтобы scope guard предоставлял пару (void*,size_t*). Клиентский код неким способом прочитает указатель на буфер, что-то там с ним сделает и запишет размер "куда следует". А затем scope guard выполнит release_buffer с этим размером.
Но переделывать клиентский код придётся основательно.
Так что проще уж перегрузить fread для работы с mem_chunk, чтоб оно само финализировало как полагается.
void fread(mem_chunk& mc, size_t elem, size_t count, FILE* fp)
{
size_t done = fread(mc.get_buffer(count*elem), elem, count, fp);
mc.release_buffer(done*elem);
}
(Ну или не перегрузить, а переименовать — тут уже на вкус и цвет).
... << RSDN@Home 1.2.0 alpha 4 rev. 1207>>