Как на C написать placement new для различных структур?
Допустим у меня есть предзаказанный буфер длиною N и я хочу внутри него разместить несколько разных экземпляров разных структур. Как это сделать правильно?
Здравствуйте, B0FEE664, Вы писали:
BFE>Допустим у меня есть предзаказанный буфер длиною N и я хочу внутри него разместить несколько разных экземпляров разных структур. Как это сделать правильно?
BFE> pBuffer->tail += sizeof(MyStruct); // _Alignof(MyStruct) ?
У вас размер структуры должен быть выравнен (paddings). Достаточно что бы начало вашего региона было выравнено.
А так можете вручную выравнивать если память линейная
Здравствуйте, kov_serg, Вы писали:
BFE>>Допустим у меня есть предзаказанный буфер длиною N и я хочу внутри него разместить несколько разных экземпляров разных структур. Как это сделать правильно? BFE>> pBuffer->tail += sizeof(MyStruct); // _Alignof(MyStruct) ? _>У вас размер структуры должен быть выравнен (paddings).
Зачем мне выравнивать размер структуры? Структуры разные и разного размера должны лежать в одном буфере. Выравнивать все по одному размеру — зря расходовать память.
_>Достаточно что бы начало вашего региона было выравнено.
разве?
_>А так можете вручную выравнивать если память линейная
Память линейная, но я не понимаю как это применять: _>
BFE>Допустим у меня есть предзаказанный буфер длиною N и я хочу внутри него разместить несколько разных экземпляров разных структур. Как это сделать правильно?
Уж, не union ли тебе нужен? Или я чего не понимаю?
Я прочитал что такое alignof и увидел, что оно возвращает что задаёт alignas. А последнее это аналог pragma pack. Т.е. Для структуры он задаёт выравнивание полей по некоторому размеру.
Т.е. размер струтуры от этого меняется, так как меняется положение полей внутри структуры. И для массива одинаковых структур, вам достаточно выравнять положение первого элемента массива. sizeof вернёт размер кратный выравниванию. И следующая структура будет тоже выравнена. Для разных структур, надо выравнивать каждую структуру.
BFE>Допустим у меня есть предзаказанный буфер длиною N и я хочу внутри него разместить несколько разных экземпляров разных структур. Как это сделать правильно?
Твой вариант в общем-то норм, но в нём будет проблема с выравниванием.
Если есть структуры struct S1 { char data; } и struct S4 { int data; }, то вызов Create_S1(), за которым следует Create_S4(), приведёт к тому, что поля второй структуры будут неверно выровнены (при допущении, что sizeof(int) > 1 и при естественном допущении, что изначально буфер был выровнен, например, из-за того, что был получен вызовом malloc).
То есть необходимо перед "выделением" памяти проверять значение tail и довыравнивать его при необходимости.
Например, как-то так:
uintptr_t uiTail = (uintptr_t)(pBuffer->tail);
uiTail = (uiTail + (_Alignof(MyStruct) - 1)) / _Alignof(MyStruct) * _Alignof(MyStruct); // тут педанты могут ещё проверить на переполнениеchar* tail = (сhar*)uiTail; // теперь дальше нужно использовать этот tail вместо (pBuffer->tail)if ( pBuffer->end < tail + sizeof(MyStruct) ) // тут формально тоже может быть переполнение, на которое можно проверитьreturn NULL;
MyStruct* pNewObj = (MyStruct*)tail;
pBuffer->tail = (char*)(pNewObj + 1);
BFE>typedef struct BFE>{ BFE> const char* begin; BFE> const char* end; BFE> const char* tail; BFE>} Buffer;
Занимательно, что функция выделения памяти хранить указатели на const, а выдаёт наружу указатели без const. Интересно, из каких соображений этот const в Buffer появился?
Здравствуйте, watchmaker, Вы писали: BFE>>Допустим у меня есть предзаказанный буфер длиною N и я хочу внутри него разместить несколько разных экземпляров разных структур. Как это сделать правильно? W>Твой вариант в общем-то норм, но в нём будет проблема с выравниванием.
Собственно, в правильном выравнивании и состоит вопрос. W>Если есть структуры struct S1 { char data; } и struct S4 { int data; }, то вызов Create_S1(), за которым следует Create_S4(), приведёт к тому, что поля второй структуры будут неверно выровнены (при допущении, что sizeof(int) > 1 и при естественном допущении, что изначально буфер был выровнен, например, из-за того, что был получен вызовом malloc). W>То есть необходимо перед "выделением" памяти проверять значение tail и довыравнивать его при необходимости.
Я так и думал. Правильно ли я понимаю, что адрес структуры должен быть кратным _Alignof(имя структуры)?
как-то так:
W>uintptr_t uiTail = (uintptr_t)(pBuffer->tail);
W>uiTail = (uiTail + (_Alignof(MyStruct) - 1)) % _Alignof(MyStruct); // тут педанты могут ещё проверить на переполнение
// ] uiTail == 20 and _Alignof(MyStruct) == 4 then uiTail == 3 ?
W>char* tail = (сhar*)uiTail; // теперь дальше нужно использовать этот tail вместо (pBuffer->tail)
// char* tail = pBuffer->tail + (сhar*)uiTail; ?
W>if ( pBuffer->end < tail + sizeof(MyStruct) ) // тут формально тоже может быть переполнение, на которое можно проверить
W> return NULL;
W>MyStruct* pNewObj = (MyStruct*)tail;
W>pBuffer->tail = (char*)(pNewObj + 1); // +1 ? Чего-то я не понимаю...
W>
W>Занимательно, что функция выделения памяти хранить указатели на const, а выдаёт наружу указатели без const. Интересно, из каких соображений этот const в Buffer появился?
Этот код из головы.
Здравствуйте, B0FEE664, Вы писали:
BFE>Зачем мне выравнивать размер структуры? Структуры разные и разного размера должны лежать в одном буфере. Выравнивать все по одному размеру — зря расходовать память.
Выравнивают не для экономии памяти, а для уменьшения количества обращений к памяти (которая читается в кэш блоками). Так что не заморачивайтесь и выравнивайте например по 16 байт все свои разные структуры.
_>>Достаточно что бы начало вашего региона было выравнено. BFE>разве?
У вас была всего одна структура. И если она кратна 4 байтам и выравнивание у вас 4 байта, то это будет сохраняться.
_>>А так можете вручную выравнивать если память линейная BFE>Память линейная, но я не понимаю как это применять: _>>
Здравствуйте, B0FEE664, Вы писали:
BFE> Правильно ли я понимаю, что адрес структуры должен быть кратным _Alignof(имя структуры)?
Да.
BFE>как-то так: BFE>const int nPadding = (pBuffer->tail — (char*)NULL) % _Alignof(MyStruct);
Нет, тут неверно.
BFE>MyStruct* Create_JsnToken(Buffer* pBuffer)
BFE>{
//Это то, на сколько указатель из размера выравнивания выбивается, а не сколько надо добавить, чтобы стать кратным _Alignof(MyStruct).
BFE> const int nPadding = (pBuffer->tail - (char*)NULL) % _Alignof(MyStruct);
BFE> if ( pBuffer->end < pBuffer->tail + nPadding + sizeof(MyStruct) )
BFE>
Здравствуйте, B0FEE664, Вы писали:
BFE>Зачем мне выравнивать размер структуры? Структуры разные и разного размера должны лежать в одном буфере. Выравнивать все по одному размеру — зря расходовать память.
В ряде случаев, выравнивание увеличивает эффективность обмена между памятью и процессором. Ещё в ряде случаев, процессор просто пошлёт программиста читать маны при первой же попытке обратиться к невыровненным данным.
Здравствуйте, kov_serg, Вы писали:
BFE>>А разве представление отрицательных чисел в дополнительном коде гарантируется стандартом C? _>Вам шашечки или ехать?
Здравствуйте, _NN_, Вы писали:
_NN>Скоро будет
Вот не очень понятно чем модульная арифметика им не угодила.
Видимо очень хотят новых граблей в виде a+b+c != a+c+b и других прелестей.
Здравствуйте, kov_serg, Вы писали:
_>Вот не очень понятно чем модульная арифметика им не угодила.
Ну написали же:
over 90% of all overflow is a bug, and defining wrapping behavior would not have solved the bug.
и пару других причин.
И сделав переполнение определённым ты лишишь санитайзеры возможности находить ошибки в коде: в исходниках ведь не написано для каждой арифметической операции является ли переполнение в ней желаемым поведением или программист просто забыл его проверить.
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, kov_serg, Вы писали:
_>>Вот не очень понятно чем модульная арифметика им не угодила. W>Ну написали же: W>
over 90% of all overflow is a bug, and defining wrapping behavior would not have solved the bug.
W>и пару других причин.
W>И сделав переполнение определённым ты лишишь санитайзеры возможности находить ошибки в коде: в исходниках ведь не написано для каждой арифметической операции является ли переполнение в ней желаемым поведением или программист просто забыл его проверить.
В отличии от модульной арифметики, арифметика которую предлагают противоречивая и содержит больше проблем чем решает. Но походы по граблям еще предстоят. Например в webassembly ввернули проверку на переполнение и получили неожиданные эффекты.
Подобная арифметика нужна только для очень векторных операций, что бы не угробить эффект от векторизации.