Здравствуйте, A.J., Вы писали:
AJ>Хочется в compile-time проверить, что никакое поле не забыли. Пришла идея сделать compile-time AJ>функцию calcMembersSize, куда передавать тот же список полей, и сравнивать результат с sizeof(S).
Если нет наследования и нет игр с выравниванием, то можно
— отсортировать указатели на члены по возрастанию (либо потребовать, чтобы они были отсортированы)
— выковырять из них типы полей: Members... ==> Types (S::*)... ==> Types...
— построить кортеж std::tuple<Types...>
Если наследование есть, то тут начинаются всякие странные странности.
Оптимизация пустой базы. Которая может быть внезапно выключена.
Указатели на таблицы виртуальных функций. Первый из которых унаследован от первой базы.
Виртуальное наследование — отдельный котелок в аду.
Поэтому найти смещение первого "своего" поля — это ещё нужно постараться.
Здравствуйте, A.J., Вы писали:
AJ>можно ли реализовать подсчет размеров полей (вместе с padding) в compile-time?
Возможности по интроспекции в C++ традиционно убогие. Даже если и удастся сколхозить сколько-нибудь универсальное решение, это будет такой чудовищно уродливый костыль, что проще (и надежнее) отдельно выписывать вручную все размеры константами, и сравнивать с ними, уповая на то, что одинаковой ошибки сразу во всех местах не случится. Да, это будет криво и примитивно, но другие решения будут еще кривее, разве что эту кривизну худо-бедно удастся замести под ковер.
Re[5]: Получить размер полей структуры в compile-time
Здравствуйте, andrey.desman, Вы писали:
S>>В качестве исходной точки и основы для вдохновения можете посмотреть на вот эту штуку, в частности вот на этот кусок.
AD>Тут уже размеры полей роли не играют по идее.
Если просуммировать размеры всех полей вместе с паддингами, то должны в итоге выйти на размер всей структуры. Если не вышли, значит либо чего-то не перечислили, либо не учли какой-то прикол с дополнительным выравниванием. Если же превысили размер структуры, то либо перечислили лишнее, либо где-то сработало empty base optimization или no_unique_address.
Как-то так. Но все это костыльные костыли в отсутствии полноценной рефлексии.
Re[10]: Получить размер полей структуры в compile-time
Здравствуйте, A.J., Вы писали:
AJ>У этой либы насколько я понимаю те же ограничения, что у Boost.PFR — не поддерживает наследование и поля ссылочного типа
Для standard layout есть offsetof. Разница offsetof соседних полей дает размер с padding и т.п.
Но неспроста offsetof не работает для произвольных типов.
Считаю, что в исходной постановке задача не решается.
Re[3]: Получить размер полей структуры в compile-time
Здравствуйте, A.J., Вы писали:
AJ>Согласен. Хорошо, допустим фиксируем порядок передачи полей на тот, в котором поля идут в объявлении структуры. AJ>И считаем, что индивидуальный alignas не используется. AJ>В этом случае задача решаема?
Возможно. По крайней мере сам недавно делал что-то похожее (но только похожее, далеко не 1-в-1). Хотя на счет учета базовых классов не уверен, особенно с учетом того, что может быть и виртуальное наследование, и фокусы со empty base optimization, и приколы от компиляторов.
Здравствуйте, A.J., Вы писали:
AJ>Подскажите, можно ли реализовать подсчет размеров полей (вместе с padding) в compile-time? AJ>... AJ>
template <typename T, typename... Members>
consteval size_t calcMembersSize(Members&&... )
{
return sizeof( std::tuple<Members...> );
// ?
}
int main() {
A a;
// Перечислять, строго, в правильном порядке порядке, включая поля базовых классов.
// Не забываем про указатель на таблицу виртуальных функций (используем (void*)nullptr),
// если есть виртуальные функции.
static_assert(calcMembersSize<A>((void*)nullptr, a.j, a.i, a.d, a.s, a.b, a.str, a.c) == sizeof(a));
static_assert(calcMembersSize<A>((void*)nullptr, a.j, a.str, a.i, a.s, a.b, a.c, a.d) == sizeof(a));
static_assert(calcMembersSize<A>((void*)nullptr, a.j, a.d, a.i, a.c, a.s, a.str, a.b) == sizeof(a));
Но, если в структуре есть ручная настройка: pragma pack, allignas и т.д., то этот прием не сработает.
AJ> Увы, определения структур менять нельзя
Хотя в С++ и нет интроспекции, это не значить что ее нельзя прикрутить как внешнею утилиту / дополнительный шаг сборки.
Например, можно генерировать внешние метаданные, в стиле boost.fusion https://www.boost.org/doc/libs/1_86_0/libs/fusion/doc/html/fusion/adapted/adapt_struct.html
или сразу ваши целевые функции.
Я использовал этот подход для генерации самопального интероп-а. Не уверен, что это целесообразно в других ситуациях.
Re[9]: Получить размер полей структуры в compile-time
Здравствуйте, andrey.desman, Вы писали:
AD>Если список полей можно получить и так, то смысла вообще что-то указывать нет. Но если есть, то нет смысла подсчитывать размеры полей если можно просто сравнить два списка
У этой либы насколько я понимаю те же ограничения, что у Boost.PFR — не поддерживает наследование и поля ссылочного типа
Есть функция classSize, принимающая объект типа S и parameter pack из всех полей S.
Parameter pack нужен, т.к. для каждого поля нужно еще вызвать метод getExtraUsedMemory(field).
Хочется в compile-time проверить, что никакое поле не забыли. Пришла идея сделать compile-time
функцию calcMembersSize, куда передавать тот же список полей, и сравнивать результат с sizeof(S).
Подскажите, можно ли реализовать подсчет размеров полей (вместе с padding) в compile-time?
Функция должна учитывать возможность наличия vtable и полей базовых классов
(поэтому не подходит Boost.PFR, который требует standard layout).
Можно предполагать 20й стандарт. Должно работать под GCC 13. Ок если под другими компиляторами работать не будет!
Сами структуры менять нельзя, в том числе навешивать на них #pragma pack(1).
#include <iostream>
template <typename T, typename... Members>
consteval size_t calcMembersSize(Members&&... members)
{
// ?
}
struct B {
virtual void v() {}
};
struct C {
virtual void w() {}
};
struct D {
int j;
};
struct E {};
struct A : B, C, D
{
int i;
double d;
short s;
bool b;
std::string str;
char c;
};
int main() {
A a;
// Перечислять можно в любом порядке, но должны быть указаны все поля
static_assert(calcMembersSize<A>(a.i, a.d, a.s, a.b, a.str, a.c) == sizeof(a));
static_assert(calcMembersSize<A>(a.str, a.i, a.s, a.b, a.c, a.d) == sizeof(a));
static_assert(calcMembersSize<A>(a.d, a.i, a.c, a.s, a.str, a.b) == sizeof(a));
static_assert(calcMembersSize<A>(a.d, a.s, a.c, a.b, a.str) != sizeof(a));
static_assert(calcMembersSize<A>(a.i, a.s, a.c, a.b, a.str) != sizeof(a));
static_assert(calcMembersSize<A>(a.i, a.d, a.c, a.b, a.str) != sizeof(a));
static_assert(calcMembersSize<A>(a.i, a.d, a.s, a.str) != sizeof(a));
static_assert(calcMembersSize<A>(a.i, a.d, a.s, a.str) != sizeof(a));
}
Заранее спасибо!
Re: Получить размер полей структуры в compile-time
AJ> // Перечислять можно в любом порядке, но должны быть указаны все поля
AJ> static_assert(calcMembersSize<A>(a.i, a.d, a.s, a.b, a.str, a.c) == sizeof(a));
AJ>
"Перечислять можно в любом порядке" -- сразу нет. Большой и жирный "нет" с заглавной буквы Ны.
Потому что если у вас есть четыре поля типа char (c1, c2, c3, c4) и четыре поля типа int (i1, i2, i3, i4), то размер вашей структуры будет сильно зависеть от того, в каком порядке эти поля перечислены в самой структуре. Так что если в структуре у вас идут c1, i1, c2, i2, c3, i3, c4, i4, а в вызове calcMemberSize -- c1, c2, c3, c4, i1, i2, i3, i4, то результаты будут ну очень сильно разные.
Кроме того, для отдельных полей структуры может выставляться индивидуальный alignas, что повлияет на выравнивание и, соответственно, на размер, но просто по имени поля вы про этот alignas не узнаете.
Re[2]: Получить размер полей структуры в compile-time
Здравствуйте, so5team, Вы писали:
S>"Перечислять можно в любом порядке" -- сразу нет. Большой и жирный "нет" с заглавной буквы Ны. S>Потому что если у вас есть четыре поля типа char (c1, c2, c3, c4) и четыре поля типа int (i1, i2, i3, i4), то размер вашей структуры будет сильно зависеть от того, в каком порядке эти поля перечислены в самой структуре. Так что если в структуре у вас идут c1, i1, c2, i2, c3, i3, c4, i4, а в вызове calcMemberSize -- c1, c2, c3, c4, i1, i2, i3, i4, то результаты будут ну очень сильно разные.
S>Кроме того, для отдельных полей структуры может выставляться индивидуальный alignas, что повлияет на выравнивание и, соответственно, на размер, но просто по имени поля вы про этот alignas не узнаете.
Согласен. Хорошо, допустим фиксируем порядок передачи полей на тот, в котором поля идут в объявлении структуры.
И считаем, что индивидуальный alignas не используется.
В этом случае задача решаема?
Re[2]: Получить размер полей структуры в compile-time
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Возможности по интроспекции в C++ традиционно убогие. Даже если и удастся сколхозить сколько-нибудь универсальное решение, это будет такой чудовищно уродливый костыль, что проще (и надежнее) отдельно выписывать вручную все размеры константами, и сравнивать с ними, уповая на то, что одинаковой ошибки сразу во всех местах не случится. Да, это будет криво и примитивно, но другие решения будут еще кривее, разве что эту кривизну худо-бедно удастся замести под ковер.
Спасибо, но не очень понимаю, что в моем случае даст выписывание вручную — весь смысл задачи в том, чтобы проконтролировать полноту перечисления полей.
Супер универсальности не нужно, достаточно покрыть самые нужные кейсы. На виртуальное наследование можно забить. На поддержку компиляторов кроме GCC можно забить.
Даже на формальное UB можно закрыть глаза, если сейчас по факту это будет работать на GCC.
Re[4]: Получить размер полей структуры в compile-time
Здравствуйте, so5team, Вы писали:
S>В качестве исходной точки и основы для вдохновения можете посмотреть на вот эту штуку, в частности вот на этот кусок.
Тут уже размеры полей роли не играют по идее. Надо ведь проверить, что передали все, а размер для этого считать не надо. С другой стороны, тогда можно и поля не передавать, а брать сразу из либы.
Штука прикольная — инжектит лямбду куда-то там, а потом берет и парсит __func__, чтобы выдернуть имя поля. Но не понятно, что у нее с требованиями по видимости и всяким наследованиям. Скорее всего тоже нужен standard layout.
Re[6]: Получить размер полей структуры в compile-time
Здравствуйте, so5team, Вы писали:
AD>>Тут уже размеры полей роли не играют по идее.
S>Если просуммировать размеры всех полей вместе с паддингами, то должны в итоге выйти на размер всей структуры. Если не вышли, значит либо чего-то не перечислили, либо не учли какой-то прикол с дополнительным выравниванием. Если же превысили размер структуры, то либо перечислили лишнее, либо где-то сработало empty base optimization или no_unique_address.
Зачем вычислять размеры полей если есть список перечисленного и актуальный список?
Зачем перечислять если можно просто взять заведомо актуальный список?
Re[3]: Получить размер полей структуры в compile-time
Здравствуйте, A.J., Вы писали:
AJ>что в моем случае даст выписывание вручную — весь смысл задачи в том, чтобы проконтролировать полноту перечисления полей.
Вот и контролируйте вручную — копируете структуру, вставляете в нужное место, и руками преобразуете определение каждого поля в проверку смещения/размера, хоть через static_assert, хоть через assert. Если в будущем кто-то поменяет параметры структуры, соответствующие assert'ы сработают.
Да, это коряво, но извращаться с убогими средствами недо-интроспекции C++ — еще корявее.
AJ>На поддержку компиляторов кроме GCC можно забить. Даже на формальное UB можно закрыть глаза, если сейчас по факту это будет работать на GCC.
Предложенный способ будет работать всегда и везде.
Re[7]: Получить размер полей структуры в compile-time
Здравствуйте, andrey.desman, Вы писали:
AD>Зачем вычислять размеры полей если есть список перечисленного и актуальный список? AD>Зачем перечислять если можно просто взять заведомо актуальный список?
Мне тоже непонятно, что такое "актуальный список". Есть только переданный список и сама структура/класс.
Re: Получить размер полей структуры в compile-time
Здравствуйте, A.J., Вы писали:
AD>>Зачем вычислять размеры полей если есть список перечисленного и актуальный список? AD>>Зачем перечислять если можно просто взять заведомо актуальный список?
AJ>Мне тоже непонятно, что такое "актуальный список". Есть только переданный список и сама структура/класс.
Актуальный список — список полей, который ты можешь получить для структуры/класса с помощью либы reflect-cpp, которую выше указали.
Задача была такая
Хочется в compile-time проверить, что никакое поле не забыли.
Если список полей можно получить и так, то смысла вообще что-то указывать нет. Но если есть, то нет смысла подсчитывать размеры полей если можно просто сравнить два списка
Re[2]: Получить размер полей структуры в compile-time