Выделение класса переменного размера
У меня есть структура данных переменной длины, многомерный итератор:
class Iterator
{
public:
static Iterator& init(int dim, int* sizes, void* mem)
{
return *(new (mem) Iterator(dim, sizes));
}
static size_t alloc_size(int dim)
{
return sizeof(Iterator) + sizeof(int) * 2 * dim;
}
void operator++()
{
// increment counters, update pos_ and done_
}
bool done() const { return done_; }
bool pos() const { return pos_; }
private:
Iterator(int dim, int* sizes) : dim_(dim), pos_(0), done_(false)
{
for (int i=0; i<dim_; ++i) size(i) = sizes[i];
for (int i=0; i<dim_; ++i) counter(i) = 0;
}
int dim_;
int pos_;
bool done_;
int size (int i) { return reinterpret_cast<int*>(this+1)[i]; }
int& counter(int i) { return reinterpret_cast<int*>(this+1)[dim_+i]; }
};
Размерность итератора не известна во время компиляции, но, вероятно, мала, поэтому я выделяю память для итератора с alloca
:
void* mem = alloca(Iterator::alloc_size(dim));
for (Iterator& i = Iterator::create(dim, sizes, mem); !i.done(); ++i)
{
// do something with i.pos()
}
Есть ли более элегантный способ выделения памяти для итератора? Я осознаю тот факт, что по возвращении из функции ее стек разматывается, поэтому alloca
должен использоваться в кадре стека вызывающей стороны (см., например, здесь). Этот ответ предполагает, что распределение будет выполнено в параметре по умолчанию:
static Iterator& init(int dim, int* sizes, void* mem = alloca(alloc_size(dim)));
Как бы элегантно это решение не помогло мне: Default argument references parameter 'dim'
, Любое предложение для хорошего решения?
4 ответа
К сожалению, учитывая, что dim
это значение времени выполнения, нет другого способа сделать это, кроме как с помощью макроса:
#define CREATE_ITERATOR(dim, sizes) \
Iterator::init(dim, sizes, alloca(Iterator::alloc_size(dim)))
Вы можете иметь параметр измерения в качестве аргумента шаблона.
Мое предложение может быть не тем, что вы ищете, но почему бы не иметь функцию create|make_iterator, которая вызывает alloca?
Я не рекомендовал бы использовать alloca вообще. Если значение dim маленькое, тогда будет достаточно некоторого буфера фиксированного размера внутри класса. Если dim велико, тогда стоимость выделения кучи будет пренебрежимо мала по сравнению со сложностью других операций, выполняемых на вашем итераторе (обратите внимание, что для очень больших значений dim alloca может вызвать переполнение стека). Вы можете выбирать между фиксированным буфером и распределением кучи во время выполнения, в зависимости от размера dim.
Поэтому я бы рекомендовал подход, похожий на оптимизацию небольших строк в std::string.
Возможно, какая-то техника COW (копирование на запись http://en.wikipedia.org/wiki/Copy-on-write) также может быть полезна для ваших итераторов.
Обратите внимание, что этот метод нельзя использовать с alloca, только с распределением кучи. Более того, почти невозможно скопировать или скопировать инициализирующие итераторы, если они используют alloca (по крайней мере, без более и более безобразных макросов).
Аллока злая:)