Здравствуйте, Seigram, Вы писали:
S>Ты на каких компиляторах пробовал самый 1-й вариант, — без workarounds?
проверял в VS2010, для чистоты эксперимента надо бы еще проверить в gcc. __alignof(), насколько я понимаю, Microsoft specific оператор, т.е. гцц про него ничего не знает, так? Если так то этот вариант не подходит.
Здравствуйте, szag, Вы писали:
S>Здравствуйте, Seigram, Вы писали:
S>>Ты на каких компиляторах пробовал самый 1-й вариант, — без workarounds?
S>проверял в VS2010, для чистоты эксперимента надо бы еще проверить в gcc. __alignof(), насколько я понимаю, Microsoft specific оператор, т.е. гцц про него ничего не знает, так? Если так то этот вариант не подходит.
Верно, верно. Выравние это прямо — тема. Вот ссылка к примеру:
см. последние 2 абзаца. т.е. все сильно зависит от компилятора(padding спереду или сзаду), вообще как на ум компилятору взбредет. Потом все перестает работатть при #pragma pack(push,N) — формулы не справились и т.д.
И поэтому, для БЕЗОПАСНОСТИ кода надо отказаться от твоей затеи. Перейти к указателям на члены. Можно сколько угодно колдовать с offsetof() но тебе придется написать кучу define's чтобы обеспечить приемственность. Опять же никто не гарантирует "подставы".
Лучьше обработай шефа на тему "безопасность и красота" требует жертв, и перепиши lib'ку. Дальше ведь работать и расширять её проще будет. Капитан Очевидность подсказывает — что делать софт гибче, удобнее и надежнее надо не каменным топором, а современными инструментами.
Здравствуйте, Seigram, Вы писали:
S> Верно, верно. Выравние это прямо — тема. Вот ссылка к примеру: S>см. последние 2 абзаца. т.е. все сильно зависит от компилятора(padding спереду или сзаду), вообще как на ум компилятору взбредет. Потом все перестает работатть при #pragma pack(push,N) — формулы не справились и т.д.
вероятность использования подобных конструкций конкретно в моей ситуации крайне мала.
S> И поэтому, для БЕЗОПАСНОСТИ кода надо отказаться от твоей затеи. Перейти к указателям на члены. Можно сколько угодно колдовать с offsetof() но тебе придется написать кучу define's чтобы обеспечить приемственность. Опять же никто не гарантирует "подставы". S> Лучьше обработай шефа на тему "безопасность и красота" требует жертв, и перепиши lib'ку. Дальше ведь работать и расширять её проще будет. Капитан Очевидность подсказывает — что делать софт гибче, удобнее и надежнее надо не каменным топором, а современными инструментами.
Вы все правильно говорите и даже переписать либу не проблема — проблема чтобы не пострадал клиентский код. А как переписать либу, чтобы сделать её гарантированно безопасной и при этом не пострадал клиентский код я не знаю.
К примеру, при текущей реализации я даже не могу обратиться из конструктора класса к одному из его мемберов, так как в конструкторе просто нет такой информации — она генерируется во время компиляции.
Вот упрощенный пример пользовательского кода:
и такого кода тьма тьмущая. Для каждого поля, в приведенном выше примере, генерируется куча кода, реально создается класс, который наследуется от базового, который шаблонный с кучей специализаций... Если подытожить, то я с вами согласен на 100%, но что делать в данной конкретной ситуации?
S>>Может кто-то покритикует или предложет что-то получше?
MS>Что будет если есть подряд 4 или 8 элементов по 1 байту?
Не будет. Как я писал выше, все поля гарантированно известных типом и не могут быть меньше нескольких десятков байт — это классы с виртуальными функциями и мемберами.
MS>>Что будет если есть подряд 4 или 8 элементов по 1 байту? S>Не будет. Как я писал выше, все поля гарантированно известных типом и не могут быть меньше нескольких десятков байт — это классы с виртуальными функциями и мемберами.
Как тогда трактовать предложение
Пока придумал вот такую конструкцию и проверил на всех смещениях от 1 до 16 — работает.
Здравствуйте, MShura, Вы писали:
MS>>>Что будет если есть подряд 4 или 8 элементов по 1 байту? S>>Не будет. Как я писал выше, все поля гарантированно известных типом и не могут быть меньше нескольких десятков байт — это классы с виртуальными функциями и мемберами.
MS>Как тогда трактовать предложение
MS>
MS>Пока придумал вот такую конструкцию и проверил на всех смещениях от 1 до 16 — работает.
MS>Как Вы проверили смещение 1 байт?
проверял 2 вещи:
struct CurrentAligment
{
enum
{
Value = offsetof(iAligment, f) - offsetof(iAligment, e) // это значение
};
};
и работоспособность проекта при выставленном смещении в настройках проекта.
Здравствуйте, szag, Вы писали:
S>Вы все правильно говорите и даже переписать либу не проблема — проблема чтобы не пострадал клиентский код. А как переписать либу, чтобы сделать её гарантированно безопасной и при этом не пострадал клиентский код я не знаю. S>К примеру, при текущей реализации я даже не могу обратиться из конструктора класса к одному из его мемберов, так как в конструкторе просто нет такой информации — она генерируется во время компиляции. S>Вот упрощенный пример пользовательского кода: S>
К>Правильно? К>Тогда кто мешает в список полей добавить не только их размеры, но и смещения (а ещё лучше — указатели-на-члены)? К>Будет там что-то вида К>
1. слишком много изменений в клиентском коде;
2. клиентский код станет писать сложнее, появится много зависимостей, как то, за был прописать поле в табличку или еще чего.
макросы эти были придуманы для того, чтобы сделать жизнь енд юзера проще, хорошо или плохо это было сделано вопрос другой, но сейчас клиентский код менять никто не даст, к тому же, он работает уже более 3-х лет, т.е. отлажен... и работает в разных организациях, которые не обрадуются такому апдейту сервера, при котором надо переписывать код...
Здравствуйте, szag, Вы писали:
S>1. слишком много изменений в клиентском коде; S>2. клиентский код станет писать сложнее, появится много зависимостей, как то, за был прописать поле в табличку или еще чего.
S>макросы эти были придуманы для того, чтобы сделать жизнь енд юзера проще, хорошо или плохо это было сделано вопрос другой, но сейчас клиентский код менять никто не даст, к тому же, он работает уже более 3-х лет, т.е. отлажен... и работает в разных организациях, которые не обрадуются такому апдейту сервера, при котором надо переписывать код...
Почему изменения в клиентском коде? Я спрашивал
1) макрос разворачивается в подобный код?
2) что мешает изменить макрос, чтобы он генерировал таблицу не только с размерами, но и смещениями?
Или же клиентский код уже скомпилирован, и его нельзя трогать?
То есть, — всё, что у нас есть, это таблица с отдельными полями. И по этой таблице надо восстановить лэяут структуры.
Тогда нужно для каждого поля знать не только его размер, но и его выравнивание. Это, по счастью, — свойство типа, а не свойство отдельного члена.
Логика более-менее простая:
int roundUp(int size, int align) { return (size+align-1)%align; }
int overall_tail = sizeof(Base); // текущий хвост структурыint overall_align = _alignof(Base); // выравнивание структурыint overall_size = min(1, roundUp(overall_tail, overall_align); // размер структуры
.....
// для каждого очередного поляint field_size, field_align; // размер и выравнивание текущего поля
assert(field_align > 0);
assert(field_size % field_align == 0);
int field_offset = roundUp(overall_tail, field_align);
overall_align = max(overall_align, field_align);
overall_tail = field_offset + field_size;
overall_size = roundUp(overall_tail, overall_align);
Всё, что надо — это добавить в интерфейс рядом с GetSize() ещё и GetAlign().
Но, всё же, с таблицей смещений будет проще и надёжнее.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, szag, Вы писали:
[skipped] К>Но, всё же, с таблицей смещений будет проще и надёжнее.
Я бы иначе написал это и думаю угодил бы запросу szag.
Собственно сообразить как компилятор и что размещает не сложно зная типы полей
Примерно так, -для простоты у меня шаблон, а у szag можно сделать табличку для замены sizeof(std::remove_all_extents<T>::type):
template <class T> int next_offset(int &offset, int &total_size) {
int x = offset;
size_t size = sizeof(T), mask = sizeof(std::remove_all_extents<T>::type) - 1,
trusted_offset = offset;
if (offset & mask) {
trusted_offset = offset = (offset + mask) & ~mask;
}
offset += size; total_size += offset - x;
return trusted_offset;
}
финальный размер тоже подровнять
total_size = (total_size + (compiler_opt_align -1)) &~(compiler_opt_align -1)
естественно в начале
int offset=0, total_size =0
p.s.. проверено на POD's включая поля растущие как typedef pod_type type_name[N]
Здравствуйте, MShura, Вы писали:
MS>Это называется, как Вы сами заметили в названии структуры, выравнивание, а не смещение.
да, извините, опечатался — имел ввиду именно выравнивание.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, szag, Вы писали:
S>>1. слишком много изменений в клиентском коде; S>>2. клиентский код станет писать сложнее, появится много зависимостей, как то, за был прописать поле в табличку или еще чего.
S>>макросы эти были придуманы для того, чтобы сделать жизнь енд юзера проще, хорошо или плохо это было сделано вопрос другой, но сейчас клиентский код менять никто не даст, к тому же, он работает уже более 3-х лет, т.е. отлажен... и работает в разных организациях, которые не обрадуются такому апдейту сервера, при котором надо переписывать код...
К>Почему изменения в клиентском коде? Я спрашивал К>1) макрос разворачивается в подобный код? К>2) что мешает изменить макрос, чтобы он генерировал таблицу не только с размерами, но и смещениями?
1) Видимо не правильно Вас понял. макрос внутри менять можно, нельзя менять клиентский код, т.е. если поменять макрос и клиентский код при этом перекомпилируется и гарантированно будет работать — то проблем нет
2) наверное только незнание того как это сделать
вот пример макроса, который генерит сам класс (выкинул кучу всего что мешает пониманию)
#define TABLE(name, meta, fields) \
class name : public ORMtableBase \
{ \
public: \
\
meta \
\
\
fields \
\
private: \
static FieldsList FieldsMap; \
}; \
ORMtableBase::FieldsList name::FieldsMap; \
т.е. видно, что филды сюда приходят ввиде последовательно идущих макросов, так как количество их не известно, может быть одно а может и 100. meta так же поток макросов.
как я и писал выше, филды достаточно стожные классы, которые генерируются макросами, а дальше шаблонами.
Здравствуйте, szag, Вы писали:
S>есть задача обращаться к полям класса по их имени, т.е. зная строку. Это строка, приходит, парсится потом собственно идет само обращение.
[skipped] S>P.S. Все эти извращения потому, что в месте использования нет информации о конечном типе, а есть только интерфейс.
В принципе решил я это для CodeGear2010 и MSVC2010. 1-й признать порадовал, — в обратном смысле слова
Фукция считает offset, для упрощения оформлена шаблоном
template <class T> int nextP(int &offset, int &total_size, bool &realign) {
int prev_offset = offset;
typedef std::remove_all_extents<T>::type U;
size_t size = sizeof(T), mask = sizeof(U) - 1, trusted_offset = offset;
int c = std::tr1::is_class<U>::value ? alignof(T) : 0;
if (c) mask = c - 1;
if (!realign && (offset & mask)) {
trusted_offset = offset = (offset + mask) & ~mask;
}
#ifdef __CODEGEARC__
if (c) realign = true; // maybe its code_gear bug#endif
offset += size; total_size += offset - prev_offset;
return trusted_offset;
}
Собственно для MSVC "костыль" c realign можно отрезать напрочь. Для [b]szag использовать таблицы вместо tr1 и std::remove_all_extents[/b]
Проверял я на таких структурах:
Здравствуйте, szag, Вы писали:
К>>1) макрос разворачивается в подобный код? К>>2) что мешает изменить макрос, чтобы он генерировал таблицу не только с размерами, но и смещениями?
S>1) Видимо не правильно Вас понял. макрос внутри менять можно, нельзя менять клиентский код, т.е. если поменять макрос и клиентский код при этом перекомпилируется и гарантированно будет работать — то проблем нет S>2) наверное только незнание того как это сделать
Буст-препроцессор может творить чудеса...
Вообще же, каркас макроса выглядит так
Дальше MEMBERS развёртывает макро-цепочку, превращая её в объявления полей, а METADATA — в метаданные (размеры, смещения).
Завтра попробую на BOOST/PP родить рабочий пример.
Здравствуйте, Seigram, Вы писали:
S>Я бы иначе написал это и думаю угодил бы запросу szag. S>Собственно сообразить как компилятор и что размещает не сложно зная типы полей
В момент итерирования типы полей неизвестны. Поля представлены интерфейсом IORMField*, у которого есть GetSize().
По одному только размеру судить о выравнивании сложно: char[8], int[2] и double имеют одинаковый размер и разное выравнивание
Поэтому придётся добавить в интерфейс GetAlign().
Ну а дальше — в реализациях — операторы sizeof и __alignof (или его рукодельная замена, не суть).
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, szag, Вы писали:
К>>>1) макрос разворачивается в подобный код? К>>>2) что мешает изменить макрос, чтобы он генерировал таблицу не только с размерами, но и смещениями?
S>>1) Видимо не правильно Вас понял. макрос внутри менять можно, нельзя менять клиентский код, т.е. если поменять макрос и клиентский код при этом перекомпилируется и гарантированно будет работать — то проблем нет S>>2) наверное только незнание того как это сделать
К>Буст-препроцессор может творить чудеса... К>Вообще же, каркас макроса выглядит так К>
К>Дальше MEMBERS развёртывает макро-цепочку, превращая её в объявления полей, а METADATA — в метаданные (размеры, смещения). К>Завтра попробую на BOOST/PP родить рабочий пример.
не уверен что правильно понял вашу идею.
fields представляет из себя поток дефайнов, вида:
т.е основная задача этого дефайна:
1. сформировать класс, с конструктором не принимающим параметры
2. перекрыть некоторые виртуальные функции, как то GetSize и некоторые функции в __VA_SRGS__, если пользователь указал что-то вроде EDITABLE(false). дальше создается поле по этому классу.
в общем-то функция GetSize сейчас уже включает в себя выравнивание поля.
Дальше вся эта хитрая конструкция обрабатывается кодом см. мой первый пост.
Здравствуйте, Seigram, Вы писали:
S>Здравствуйте, Кодт, Вы писали:
К>>Здравствуйте, szag, Вы писали: S>[skipped] К>>Но, всё же, с таблицей смещений будет проще и надёжнее.
S>Я бы иначе написал это и думаю угодил бы запросу szag. S>Собственно сообразить как компилятор и что размещает не сложно зная типы полей S>Примерно так, -для простоты у меня шаблон, а у szag можно сделать табличку для замены sizeof(std::remove_all_extents<T>::type):
S>p.s.. проверено на POD's включая поля растущие как typedef pod_type type_name[N]
Извините, но я что-то с первого раза не понял, чем ваша конструкция принципиально отличается от того варианта который сейчас сделал я — sizeof() + Aligment, см. посты ниже.
Это очевидно.
S>каждый дефайн вызывает другой дефайн,
А вот здесь мы вас поправим! Пусть этот дефайн не сам вызывает макрос FIELD(.....), а его извне заставляют вызвать тот или иной макрос.
И у нас есть два контекста:
— объявление членов структуры (то, что сейчас уже делается)
— заполнение массива метаданных
S> который формирует класс делает следующее:
S>#define FIELD(TypeID, RealType, name, captionID, ...) \
S>class RealType ## _ ## name : public ORMfield<TypeID, RealType> \
Во-первых, гарантируем, что склеиваться будет окончательное значение name. Во-вторых, просто нагляднее, и избегаем повторения операции (она встречается здесь четырежды: в имени класса, в конструкторе, в sizeof и в типе переменной).
S>в общем-то функция GetSize сейчас уже включает в себя выравнивание поля.
По-моему, это неправильно. В размер нужно добавлять величину зазора между этим полем и следующим. А тип следующего поля неизвестен.
Хотя... может быть, и можно так делать.