После получения кросс-платформенного проекта для компиляции у меня возникают самые странные ошибки в OSX. Программа падает по-разному (но иногда может выжить, чтобы показать, что это пользовательский интерфейс). Проходя через отладчик XCode, я вижу несколько мест, где значения подобъектов менять в зависимости от контекста. Это небольшая проблема у меня:
class third
{
public:
int some_data;
void do_something()
{
}
};
class second
{
public:
third * thirdPtr;
second()
: thirdPtr(nullptr)
{
thirdPtr = new third();
printf("second = 0x%X, third = 0x%X", this, thirdPtr);
}
};class first
{
second * secondInstance;
first()
: secondInstance(nullptr)
{
secondInstance = new second();
printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
// (maybe) crash
secondInstance->thirdPtr->do_something();
}
};
Проходя, я добавил точку наблюдения к третьему указателю, это пример вывода:
Watchpoint 1 hit:
old value: 0x00000001
new value: 0x00000000
Watchpoint 1 hit:
old value: 0x00000000
new value: 0x04821c34
И вот стандартный вывод программы:
second = 0x4821C20, third = 0x4821C34
second = 0x4821C20, third = 0x3404821C
Я никогда не видел ничего подобного. Очевидно, что структуры являются более сложными и используют наследование, но ничто другое не имеет доступа или записи в эти структуры при инициализации. У меня так много странных сбоев и проблем в моем исходном коде, поэтому я предполагаю, что этот сбой является проблемой в нескольких местах. Напоминаем, что исходный код долгое время работал безупречно на других платформах, и он дает сбой даже во внешнем библиотечном коде (я использую juce для проекта).
Сначала я подозревал, что оператор new является поддельным, но, похоже, что-то связано с составом объектов. Интересно, что две разные трети имеют сильное сходство.
Я действительно в растерянности, я готов заключить, что llvm переставляет расположение структур между компилируемыми модулями (классы / структуры находятся в отдельных файлах, конечно) … или что-то в этом роде. Я предполагаю, что я не прав — но кто-нибудь когда-либо испытывал что-то подобное?
——— редактировать ———-:
Поэтому я добавил этот код:
(внутри второго конструктора):
{
thirdPtr = new third();
printf("second = 0x%X, third = 0x%X", this, thirdPtr);
printf("position = %d", offsetof(second, thirdPtr);
}
(внутри первого конструктора):
{
secondInstance = new second();
printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
printf("position = %d", offsetof(second, thirdptr));
}
И это печатает …
13
16
Примечание: это вывод полных структур (т. Е. Не приведенных здесь примеров). НО: Структура структур на самом деле различается в зависимости от того, в каком модуле компиляции / перевода я нахожусь. Что, черт возьми, здесь происходит?
——— редактировать 2 ———:
Поэтому я решил проверить выравнивание, это может быть ключевой проблемой:
stdout изнутри второй конструктор:
second = 0x3649090, third = 0x36490A4
offset of third in second = 16
sizeof second = 232
align of third = 4, align of second = 4
стандартный вывод из первого конструктора:
second = 0x3649090, third = 0xA4036490
offset of second in third = 13
sizeof second = 226
align of third 1, align of second 1
Так что, похоже, выравнивание меняется в единицах перевода. Что я могу сделать, чтобы обеспечить соблюдение стандарта во всем проекте?
редактировать 3: мне удалось заставить его «работать», как в, он не падает сразу, применяя атрибут((упаковано)) во второй класс. Но это действительно не оставляет меня в безопасности, и почему я должен это делать? Существует ли глобальный параметр, который управляет этим параметром в единицах перевода?
—— редактировать 4: РЕШЕНИЕ —-
Включенный заголовок содержал несоответствующую директиву #pragma pack (которую поддерживает llvm) следующим образом:
#ifdef __MSVC__
#pragma pack(push, 1)
#pragma warning(disable:4482)
#else
#pragma pack (push, 1)
#endif
....
#if defined(__MSVC__)
#pragma pack(pop)
#endif
Стек упаковки будет изменен любым компилятором, но будет восстановлен только при использовании компилятора msvc ++. Это оставило упаковку всего проекта до 1.
Стало очевидным, насколько важно сбросить упаковочный стек при работе с пользовательским выравниванием уплотнения конструкций.
Компилятор Visual Studio и внешний интерфейс clang для llvm поддерживают один и тот же синтаксис с использованием директив #pragma, которые можно изучить здесь:
http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx
В моем случае включенный заголовок содержал несоответствующую директиву #pragma pack примерно так:
#ifdef __MSVC__
#pragma pack(push, 1)
#else
#pragma pack (push, 1)
#endif
....
#if defined(__MSVC__)
#pragma pack(pop)
#endif
Стек упаковки будет изменен любым компилятором, но будет восстановлен только при использовании компилятора msvc ++. Это оставило выравнивание упаковок блоков перевода, которые включали этот файл, в отличие от тех, которые этого не делали, даже если оба блока перевода видели одно и то же определение структуры. Для полноты вот (в моем случае) исправленные директивы #pragma:
#if defined(__MSVC__) || defined (__LLVM__)
#pragma pack (push, 1)
#endif
#if defined(__MSVC__) || defined (__LLVM__)
#pragma pack(pop)
#endif
Других решений пока нет …