Массив переменной длины
От: enji  
Дата: 30.11.11 05:07
Оценка:
struct Data 
{
  int a;
  int b;
  std::string c;
};

struct Base
{
  int sz;  
  Data d[1];
};

void doSomething(Base *b)
{
  for (int i = 0; i < b.sz; ++i)
  {
    std::cout << b->d[i].a << b->d[i].s << '\n';  /// *************
  }
}

struct Der11
{
  Base b;
  Data d[10];
};

void func()
{
  Der10 d;
  d.sz = 11;
  doSomething(&d.b);
}


Можно ли по стандарту так делать? А если Data будет POD? Должна ли Base быть POD? A Der11? А если в Base будет еще какой-нить double — не будет ли проблем с выравниванием?

Спасибо!
Re: Массив переменной длины
От: johny5 Новая Зеландия
Дата: 30.11.11 05:33
Оценка:
Здравствуйте, enji, Вы писали:

E>
E>struct Data {};
E>struct Base
E>{
E>  int sz;  
E>  Data d[1];
E>};

E>struct Der11
E>{
E>  Base b;
E>  Data d[10];
E>};

E>


E>Можно ли по стандарту так делать? А если Data будет POD? Должна ли Base быть POD? A Der11? А если в Base будет еще какой-нить double — не будет ли проблем с выравниванием?


О боже, какие страшные хаки. Вот только недавно встретил в WINAPI такое же, полчаса думал что это и как они до такого додумались. Вот откуда оказывается ноги то расли

Если вам хочется задавать кол-во элементов в зависимости от класса наследника, вот вам несколько путей:

Динамический:


struct Base
{
  std::vector<Data>  d;
};

void func()
{
  Der10 d;
  d.d.resize(10);
  doSomething(&d.b);
}



Статический:

template<int N>
struct Base
{
  Data  d[N];
};

struct Der10 : public Base<10>
{};

template<int N>
void doSomething(Base<N> *b)
{
  for (int i = 0; i < N; ++i)
  {
    std::cout << b->d[i].a << b->d[i].s << '\n';  /// *************
  }
}

void func()
{
  Der10 d;
  doSomething(&d.b);
}
Re[2]: Массив переменной длины
От: enji  
Дата: 30.11.11 06:04
Оценка:
Здравствуйте, johny5, Вы писали:

J>Если вам хочется задавать кол-во элементов в зависимости от класса наследника, вот вам несколько путей:


J>Динамический:



J>
J>struct Base
J>{
J>  std::vector<Data>  d;
J>};
J>


У меня не везде есть stl и даже куча

Можно сделать так (у меня счас так и сделано):
struct Base
{
 Data *d;
 int sz;
};

struct Der11
{
  Der11() { d = dt; sz = 11; }
  Data dt[11];
};


но захотелось сэкономить указатель


J>Статический:


J>
J>template<int N>
J>struct Base
J>{
J>  Data  d[N];
J>};

J>struct Der10 : public Base<10>
J>{};

J>template<int N>
J>void doSomething(Base<N> *b)
J>


doSomething становится шаблонной — т.е. должна жить в хидере. Одна может быть большой, она может дергать другие методы, она может иметь много зависимостей, которые тоже придется вытянуть в хидер — в общем делать base шаблонным не хочется.

Можно как-то вот так извернуться:
struct OtherBaseData
{
  int a, b, c;
};

template<int N>
struct Base
{
  OtherBaseData obd;
  Data  d[N];
};

void int_doSomething(OtherBaseData *obd, Data *d, int d_sz);

template<int N>
void doSomething(Base<N> *b) { int_doSomething(&b->obd, b->d, N); }

Но если Base помимо массива содержит еще данные, то придется с ними приседать дополнительно.
Re[3]: Массив переменной длины
От: johny5 Новая Зеландия
Дата: 30.11.11 07:32
Оценка:
Здравствуйте, enji, Вы писали:

E>У меня не везде есть stl и даже куча

E>но захотелось сэкономить указатель

Увидел в первоначальном примере std::string, предположил что нормальный С++, но если С++ у вас коцанный, сгодиться и такой вариант, с указателем. Если размер указателя конечно не критичен. С align-ами возиться я бы не стал просто из чуства гигиены.

Можно кстате align задать явно (#pragma pack, __declspec..), например если задать упаковку в 1 байт на обе структуры Data и Base, то в вашем первоначальном варианте "подсоединённая" Data гарантировано ляжет за первой. Впрочем выравнивание в 1 байт это достаточно серьёзный удар про производительности, по другим комбинациям может подскажет кто то ещё — тут я не знаю.


J>>Статический:


E>doSomething становится шаблонной — т.е. должна жить в хидере. Одна может быть большой, она может дергать другие методы, она может иметь много зависимостей, которые тоже придется вытянуть в хидер — в общем делать base шаблонным не хочется.


E>void int_doSomething(OtherBaseData *obd, Data *d, int d_sz);


E>template<int N>

E>void doSomething(Base<N> *b) { int_doSomething(&b->obd, b->d, N); }
E>[/ccode]

Либо так либо просто декларировать void doSomething(Base<N> *b); без имплементации. Имплементацию разместить в .cpp и явно инстанциировать шаблон там же, т.е.:


.h:
template<int N>
void doSomething(Base<N> *b);

.cpp:
template<int N>
void doSomething(Base<N> *b)
{
    .. bzzz
}

template void doSomething<1>(Base<1>* );
template void doSomething<10>(Base<10>* );
Re: Массив переменной длины
От: MasterZiv СССР  
Дата: 30.11.11 08:38
Оценка: +3
On 11/30/2011 09:07 AM, enji wrote:

> Можно ли по стандарту так делать?


Понятие "так можно делать по стандарту" не определено.
Можешь делать, вопрос в том, что будет после этого.

Ты можешь так делать, если сам будешь правильно выделять память
под массив переменного размера и сам будешь вызывать конструктры
всех элементов. (и деструкторы также).


На счёт Der11 -- размер Base формально определён, но фактически -- переменный.
Как ты его хочешь зафигачить в другой класс мембером (или предком) --
не понятно. Максимум что можно с ним делать -- хранить на него
ссылку (указатель или ссылку).

А если Data будет POD?

Если это POD, то соответственно конструкторы и деструкторы вызывать
не надо, потому что их просто нет.

Должна ли Base быть
> POD?
Нет, см. выше.

A Der11?

Der11 вообще некорректно. Не с точки зрения С++, а просто по жизни.
См. выше.

А если в Base будет еще какой-нить double — не будет ли проблем с
> выравниванием?

нет. Если правильно будешь выделять память.


На самом деле самый большой вопрос -- а занафига.
Потому что если это у тебя С++, то проще держать std::vector и не парица,
а если это С, то тогда с какого фига там в элементе std::string ?
Posted via RSDN NNTP Server 2.1 beta
Re: Массив переменной длины
От: k.o. Россия  
Дата: 30.11.11 09:49
Оценка:
Здравствуйте, enji, Вы писали:

E>Можно ли по стандарту так делать? А если Data будет POD? Должна ли Base быть POD? A Der11? А если в Base будет еще какой-нить double — не будет ли проблем с выравниванием?


По стандарту нет гарантии, что между Der11::b и Der11::d отсутствует padding, да и выход за границы массива уже UB, с другой стороны, на практике это должно работать. По большому счету, такой трюк ничем не отличается от "struct hack" гарантированно работающего как в gcc так и в MSVC.
Re: Массив переменной длины
От: watch-maker  
Дата: 30.11.11 10:58
Оценка:
Здравствуйте, enji, Вы писали:

E>Можно ли по стандарту так делать?

btw,
в стандарте языка C описаны flexible array member (см. пункт 6.7.2.1). Соответственно там есть гарантии и всё такое. При этом в некоторых С++ компиляторах flexible array member поддерживается как расширение и для С++. То есть если у вас ограниченный список используемых компиляторов, то можно просто посмотреть в документации. Или, как вариант, можно скомпилировать нужный участок кода C-компилятором (хотя конечно с std::string это сделать не получится).
Re[2]: Массив переменной длины
От: enji  
Дата: 01.12.11 11:21
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>On 11/30/2011 09:07 AM, enji wrote:


>> Можно ли по стандарту так делать?


MZ>Понятие "так можно делать по стандарту" не определено.

MZ>Можешь делать, вопрос в том, что будет после этого.

MZ>Ты можешь так делать, если сам будешь правильно выделять память

MZ>под массив переменного размера и сам будешь вызывать конструктры
MZ>всех элементов. (и деструкторы также).


MZ>На счёт Der11 -- размер Base формально определён, но фактически -- переменный.

MZ>Как ты его хочешь зафигачить в другой класс мембером (или предком) --
MZ>не понятно. Максимум что можно с ним делать -- хранить на него
MZ>ссылку (указатель или ссылку).

Размер Base фиксирован, а дополнительные элементы массива лежат в Der11. При этом работающие с указателем на Base, обращаясь скажем к Base::d[5], фактически обращаются к Der11::d[4].

Вопрос собственно заключался в том, не вставит ли компилятор какие-то свои поля между Der11:b и Der11::d и обязательно ли b будет предшествовать d. Как я понимаю, тут есть нюансы, в зависимости от того POD это или не POD.

MZ>На самом деле самый большой вопрос -- а занафига.

У меня С++ в условиях дефицита памяти, отсутствия кучи и stl. Была мысль немного сэкономить. Однако хочется этот код компилить и для десктопа, не заморачиваясь при этом с подавлением выравнивания и паддинга

MZ>Потому что если это у тебя С++, то проще держать std::vector и не парица,

MZ>а если это С, то тогда с какого фига там в элементе std::string ?
std::string — просто как пример объекта с конструктором и деструктором. На самом деле там объект какого-то моего класса.
Re[3]: Массив переменной длины
От: k.o. Россия  
Дата: 01.12.11 13:38
Оценка: 6 (1)
Здравствуйте, enji, Вы писали:

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


MZ>>On 11/30/2011 09:07 AM, enji wrote:


>>> Можно ли по стандарту так делать?


MZ>>Понятие "так можно делать по стандарту" не определено.

MZ>>Можешь делать, вопрос в том, что будет после этого.

MZ>>Ты можешь так делать, если сам будешь правильно выделять память

MZ>>под массив переменного размера и сам будешь вызывать конструктры
MZ>>всех элементов. (и деструкторы также).


MZ>>На счёт Der11 -- размер Base формально определён, но фактически -- переменный.

MZ>>Как ты его хочешь зафигачить в другой класс мембером (или предком) --
MZ>>не понятно. Максимум что можно с ним делать -- хранить на него
MZ>>ссылку (указатель или ссылку).

E>Размер Base фиксирован, а дополнительные элементы массива лежат в Der11. При этом работающие с указателем на Base, обращаясь скажем к Base::d[5], фактически обращаются к Der11::d[4].


E>Вопрос собственно заключался в том, не вставит ли компилятор какие-то свои поля между Der11:b и Der11::d и обязательно ли b будет предшествовать d. Как я понимаю, тут есть нюансы, в зависимости от того POD это или не POD.


Стандарт не дает гарантии отсутствия padding'а между Der11::b и Der11::d, с другой стороны, можно ожидать, что требования по выравниванию для Base::d и Der11::d одинаковые и эти поля будут идти последовательно. Для большей надежности это можно проверить assert'ами времени компиляции:

static_assert((std::is_standard_layout<Der11>::value && (offsetof(Der11, Der11::d) == sizeof(Base))), "invalid layout");


проверка для C++11, но можно изобразить что-то похожее и для предыдущего стандарта.

По стандарту, расположение членов класса зависит от того является-ли класс standard-layout классом. Ответ на этот вопрос зависит от того является-ли standard-layout std::string, каких-либо гарантий на этот счет в стандарте нет, с другой стороны, можно ожидать, что любая разумная реализация будет отвечать этому требованию. Впрочем, если у тебя не std::string, а свой класс, можешь сам проверить выполнение нужных требований:

A standard-layout class is a class that:
— has no non-static data members of type non-standard-layout class (or array of such types) or reference,
— has no virtual functions (10.3) and no virtual base classes (10.1),
— has the same access control (Clause 11) for all non-static data members,
— has no non-standard-layout base classes,
— either has no non-static data members in the most derived class and at most one base class with
non-static data members, or has no base classes with non-static data members, and
— has no base classes of the same type as the first non-static data member.

Re[3]: Массив переменной длины
От: MasterZiv СССР  
Дата: 02.12.11 06:14
Оценка:
On 12/01/2011 03:21 PM, enji wrote:

> Размер Base фиксирован, а дополнительные элементы массива лежат в Der11. При

> этом работающие с указателем на Base, обращаясь скажем к Base::d[5], фактически
> обращаются к Der11::d[4].


Ты кажется ничего не понял.

struct Der11
{
Base b;
Data d[10];
};

Размер Base не определён. Формально sizeof(Base) будет определён для
размера массива в 1 элемент. Фактически -- кол-во элементов переменное.
Base нельзя ни агрегировать, ни вкладывать в массив или вектор по значению.
Это -- структура переменной длины.

> Вопрос собственно заключался в том, не вставит ли компилятор какие-то свои поля

> между Der11:b и Der11::d и обязательно ли b будет предшествовать d. Как я
> понимаю, тут есть нюансы, в зависимости от того POD это или не POD.

Это дело 10е и решается.

> У меня С++ в условиях дефицита памяти, отсутствия кучи и stl. Была мысль немного

> сэкономить. Однако хочется этот код компилить и для десктопа, не заморачиваясь
> при этом с подавлением выравнивания и паддинга

Легче будет туда закинуть какой-то из STL, какую-то реализацию.
Я не верю, что это невозможно.

Хотя конечно могут быть другие причины для таких решениий.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Массив переменной длины
От: k.o. Россия  
Дата: 02.12.11 07:48
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>On 12/01/2011 03:21 PM, enji wrote:


>> Размер Base фиксирован, а дополнительные элементы массива лежат в Der11. При

>> этом работающие с указателем на Base, обращаясь скажем к Base::d[5], фактически
>> обращаются к Der11::d[4].


MZ>Ты кажется ничего не понял.


MZ>struct Der11

MZ>{
MZ> Base b;
MZ> Data d[10];
MZ>};

MZ>Размер Base не определён. Формально sizeof(Base) будет определён для

MZ>размера массива в 1 элемент. Фактически -- кол-во элементов переменное.
MZ>Base нельзя ни агрегировать, ни вкладывать в массив или вектор по значению.
MZ>Это -- структура переменной длины.

struct Base
{
  int sz;  
  Data d[1];
};


С какой стати это структура переменной длины? Размер хоть и зависит от реализации, всё равно, фиксированный, и никаких проблем с агрегацией нет.
Re[4]: Массив переменной длины
От: enji  
Дата: 02.12.11 13:09
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Ты кажется ничего не понял.


MZ>struct Der11

MZ>{
MZ> Base b;
MZ> Data d[10];
MZ>};

MZ>Размер Base не определён. Формально sizeof(Base) будет определён для

MZ>размера массива в 1 элемент. Фактически -- кол-во элементов переменное.
MZ>Base нельзя ни агрегировать, ни вкладывать в массив или вектор по значению.
MZ>Это -- структура переменной длины.

Это все так. Однако Der11 — это как раз способ создать Base из 11 элементов. Это не некая самостоятельная структура, просто аналог malloc(sizeof(Base) + sizeof(Data) * 10) на стеке. Все остальные части программы работают с Base по указателю\ссылке, а про Der11 не знают.


>> Вопрос собственно заключался в том, не вставит ли компилятор какие-то свои поля

>> между Der11:b и Der11::d и обязательно ли b будет предшествовать d. Как я
>> понимаю, тут есть нюансы, в зависимости от того POD это или не POD.

MZ>Это дело 10е и решается.


Дык вопрос как раз об этом — можно ли это просто и кроссплатформенно решить? Если да — то как, какие требования к структурам-участникам?

>> У меня С++ в условиях дефицита памяти, отсутствия кучи и stl. Была мысль немного

>> сэкономить. Однако хочется этот код компилить и для десктопа, не заморачиваясь
>> при этом с подавлением выравнивания и паддинга

MZ>Легче будет туда закинуть какой-то из STL, какую-то реализацию.


Ты смеешься. Я иногда каждый байт ОЗУ считаю, а ты мне предлагаешь кучу организовать и STL заюзать
Собственно вопрос возник, когда я подумал, а нельзя ли безопасно сэкономить 2 байта на указателе

MZ>Я не верю, что это невозможно.

Возможно конечно, просто надо менять железо. Однако так как поддержку старого железа никто не отменял, то ближайшие лет 10 еще придется мучаться с имеющимся
Re[5]: Массив переменной длины
От: MasterZiv СССР  
Дата: 02.12.11 18:12
Оценка:
On 12/02/2011 05:09 PM, enji wrote:

> Дык вопрос как раз об этом — можно ли это просто и кроссплатформенно решить?

> Если да — то как, какие требования к структурам-участникам?

Выравнивание -- штука пратформозависимая, как она может решаться кроссплатформенно ?

Пишеш #ifdef -ы и несколько вариантов кода.

егче будет туда закинуть какой-то из STL, какую-то реализацию.
>
> Ты смеешься. Я иногда каждый байт ОЗУ считаю, а ты мне предлагаешь кучу
> организовать и STL заюзать
> Собственно вопрос возник, когда я подумал, а нельзя ли безопасно сэкономить 2
> байта на указателе

Так STL -- не значит "много памяти".
Posted via RSDN NNTP Server 2.1 beta
Re[6]: Массив переменной длины
От: enji  
Дата: 03.12.11 05:51
Оценка:
Здравствуйте, MasterZiv, Вы писали:


MZ>Выравнивание -- штука пратформозависимая, как она может решаться кроссплатформенно ?

MZ>Пишеш #ifdef -ы и несколько вариантов кода.

k.o. выше порекомендовал статические проверки к примеру. Для ПОД, как я понимаю, стандарт что-то гарантирует.
#ifdef конечно решение, но если их много, становится страшно

MZ>Так STL -- не значит "много памяти".


STL означает кучу. Если (как в моем случае) перечень живущих объектов и предельные размеры буферов известны, то глобальные объекты и стек всяко выгоднее кучи.
Кроме того, стл — это библиотека общего назначения, в каждом конкретном случае, если делать руками, можно сэкономить. Например, если знать что количество элементов в конкретном массиве — заведомо не более 100, то вот уже 3 байта экономии на размере.
Re[7]: Массив переменной длины
От: johny5 Новая Зеландия
Дата: 08.12.11 12:16
Оценка:
E>Например, если знать что количество элементов в конкретном массиве — заведомо не более 100, то вот уже 3 байта экономии на размере.

И потеря 1кб++ байт на неиспользуемых элементах. Кроме того нужно ещё знать где заканчиваются "нужные" элементы.
Re[8]: Массив переменной длины
От: enji  
Дата: 09.12.11 18:39
Оценка:
Здравствуйте, johny5, Вы писали:


E>>Например, если знать что количество элементов в конкретном массиве — заведомо не более 100, то вот уже 3 байта экономии на размере.


J>И потеря 1кб++ байт на неиспользуемых элементах. Кроме того нужно ещё знать где заканчиваются "нужные" элементы.


это ты о чем?
Re[9]: Массив переменной длины
От: johny5 Новая Зеландия
Дата: 10.12.11 03:20
Оценка:
E>>>Например, если знать что количество элементов в конкретном массиве — заведомо не более 100, то вот уже 3 байта экономии на размере.

J>>И потеря 1кб++ байт на неиспользуемых элементах. Кроме того нужно ещё знать где заканчиваются "нужные" элементы.


E>это ты о чем?


Ну я всмысле что "не более 100" означает 100 и менее. Это стандартная Си практика, создавать статический массив с размеров под макисмально возможное кол-вом элементов в нём. Хотя при динамической природе данных, такие массивы редко бывают заполнены даже на половину. А это и есть чистая растрата памяти.
Re[10]: Массив переменной длины
От: enji  
Дата: 14.12.11 09:43
Оценка: 1 (1)
Здравствуйте, johny5, Вы писали:



E>>>>Например, если знать что количество элементов в конкретном массиве — заведомо не более 100, то вот уже 3 байта экономии на размере.


J>>>И потеря 1кб++ байт на неиспользуемых элементах. Кроме того нужно ещё знать где заканчиваются "нужные" элементы.


E>>это ты о чем?


J>Ну я всмысле что "не более 100" означает 100 и менее. Это стандартная Си практика, создавать статический массив с размеров под макисмально возможное кол-вом элементов в нём. Хотя при динамической природе данных, такие массивы редко бывают заполнены даже на половину. А это и есть чистая растрата памяти.


Это конечно да, однако если есть задача, в которой известны предельные размеры каждого массива, то много проще создать их статически. При этом по крайней мере компилятор сможет проверить достаточность ОЗУ и не будет ошибок с нехваткой памяти, которые кстати могут проявляться лишь при определенном (не найденном при тестировании) стечении обстоятельств.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.