2014-09-26 3 views
1

Я хотел бы избежать, чтобы попасть в XY trap так вот исходная задача:Размер типов C++ с различными компиляторами

У нас есть небольшая программа, которая создает сегмент разделяемой памяти на ПК. Эта программа создает его, читая его структуру из своего файла заголовка (связка индивидуального и вложенного определения структуры). В основном только файл .h и .cpp. Эта программа будет скомпилирована g ++.

Мы хотели бы создать другую программу, средство просмотра общей памяти, которая отображает макет этой памяти в древовидной структуре. Для этого нам нужно проанализировать вышеупомянутый заголовочный файл и вычислить смещения для чтения/управления содержимым определенной части разделяемой памяти. Мы не хотим писать парсер, если это не обязательно, потому что заголовочный файл содержит дополнительные объявления и определения. Эта программа будет скомпилирована той же версией g ++, что и предыдущая программа.

Первоначально мы хотели использовать gccxml во второй программе для анализа файла заголовка, но он основан на 4.2 gcc и не может разобрать включенные заголовочные файлы, содержащие код C++ 11. Другая идея - использовать libclang для получения структуры этого файла заголовка. libclang также содержит информацию о размерах, но я не знаю, является ли размер типов и добавление/выравнивание одинаковым в случае g ++ и clang.

Мой вопрос: можете ли вы предположить, что размер типов C++ и заполнение/выравнивание структур будут одинаковыми при компиляции кода с помощью clang и g ++? Окружающая среда (ПК, ОС) одинакова. Боюсь, мы не можем, потому что стандарт C++ не определяет точные размеры типов.

Вы знаете другое решение исходной проблемы?

+4

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

+0

Я даже не понимаю, почему вы хотите иметь другую программу - почему бы просто не использовать переключатель командной строки для программы «создать» для работы в режиме «печать/управление деревом»? Если вы настаиваете на второй программе - поместите определения классов в заголовок (если их еще нет) и включите вторую программу. Разумеется, с определениями структуры/класса. нетрудно снова подключиться к сегменту разделяемой памяти и создать указатели struct/class для интерпретации содержимого памяти с помощью ...? –

ответ

0

Размер данных зависит от платформы и платформы. Вместо жесткого кодирования, оператор использования SizeOf, чтобы выяснить, подходящий размер, применимый для целевой платформы, например,

sizeof(int) 
sizeof(char) 
sizeof(double) 

т.д.

+1

Вопрос несколько более подробный, чем если бы вы его внимательно прочитали. –

1

На самом деле, в данном конкретном случае, вы должны быть хорошо.

Схема памяти структур данных является частью ABI (Application Binary Interface), а gcc и clang - следуют за Itanium ABI на x86 (и x86_64). Поэтому, запрещая ошибки, и если они компилируются для x86 или x86_64, они должны иметь двоичные совместимые типы.


В общем случае, вы, как правило, обманывают:

  1. Использование упакована структуру данных: struct X { ... } __attribute__((packed)) __attribute__((aligned (8))); и вы полностью контролировать расположение структуры памяти

  2. Как отметил Альф, иметь один компилятор, отталкивать смещение каждого элемента и использовать его для подачи генерации структур для второго компилятора

  3. Другое?

+0

Обратите внимание, что использование экземпляров любых зависимых от компилятора классов в качестве переменных-членов (например, 'std :: string' или' std :: list') может изменить макет памяти инкапсулирующей структуры. Если вы используете базовые типы, вы должны быть в порядке. – rubenvb

+0

@ rubenvb: Не совсем. 1/Те, которые не являются специфичными для компилятора, Clang может отлично скомпилировать libstdC++ и libC++; вы правы, важно использовать те же классы, очевидно. 2/Будь осторожен, что 'std :: string' или' std :: list' не могут использоваться «как есть» в сегменте общей памяти, так как они будут указывать на локальную память для процесса; даже переопределение распределителя не обязательно достаточно (это если сегмент всегда отображается на тот же адрес). Boost предоставляет расширенные типы ([для этого] (http://www.boost.org/doc/libs/1_56_0/doc/html/interprocess/allocators_containers.html)) –

0

Если вы используете фиксированной ширины целочисленных типов (http://en.cppreference.com/w/cpp/types/integer) в C-стиле структуры и организовать элементы в порядке убывания размера (то есть крупнейшие государства-члены первой), это должно быть довольно безопасно.

2

Короткий ответ: поскольку clang имеет цель «быть совместимым с gcc» (для C и C++), я бы сказал, что вы можете ожидать, что он будет создавать одинаковые смещения и размеры для одного и того же кода.

Длинный ответ: Предполагая, что вы используете только основные типы (int, short, double, char и указатели этих типов), и мы ограничение на НКУ и звоном (и их C версии ++), сохраняя в то же самое OS и той же битностью (32- или 64-разрядная на «с обеих сторон»), а затем подвержены действительным ошибкам в компиляторе, она должна иметь тот же макет структуры.

Конечно, это длинный список ограничений, и, конечно же, «вопрос с реальными ошибками» является бесконечной проблемой в этих случаях.

Вы можете сделать свой случай немного проще, если вы используете определенные типы размеров, такие как uint32_t, а не int - наоборот, если вы поместите член класса в структуру, в которой есть виртуальные участники, вы серьезно пострадали - но это не очень хорошо работает с общей памятью, так как это не гарантируется в одном месте в разных приложениях.

Будьте осторожны с функциональностью STL - вы не можете получить одну и ту же библиотеку C++ для двух компиляторов (вы можете или не можете, в зависимости от того, как вы ее установили).

Я бы дважды проверял, добавив некоторый код для печати смещения и размера важных членов (и, конечно же, запустите оба компилятора) - не забудьте сделать это для членов внутри некоторой структуры, так как это вполне может состоять в том, что общий размер структуры может быть идентичным, а контент может быть в разных смещениях.

(Как уже сказал, я видел проекты, в которых некоторый код генерируется с помощью скрипта, который печатает смещения членов структуры, и это используется в качестве входных данных для других программ в рамках проекта)

0

Я думаю, Я понимаю вашу проблему. Это то, что делает хром

COMPILE_ASSERT(sizeof(double) == 8, Double_size_not_8); 

Предполагается, что размеры будут соответствовать, но проверяет только, чтобы убедиться.

COMPILE_ASSERT - макро. You can find the definition here, но короткая версия - это то, что он говорит. Утверждение, которое происходит во время компиляции.

Если размеры не совпадают, то один из способов борьбы с ним состоит в том, чтобы определить ваш заголовок только в байтах. Вместо того, чтобы, например

struct SomeBinaryFileHeader { 
    int version; 
    int width; 
    int height; 
}; 

Вы можете сделать это

struct SomeBinaryFileHeaderReadWriteVersion { 
    uint_8 version_0; 
    uint_8 version_1; 
    uint_8 version_2; 
    uint_8 version_3; 
    uint_8 width_0; 
    uint_8 width_1; 
    uint_8 width_2; 
    uint_8 width_3; 
    uint_8 height_0; 
    uint_8 height_1; 
    uint_8 height_2; 
    uint_8 height_3; 
} 

и т.д., а затем конвертировать из одного в другой, который будет работать даже через

байтов
+0

Примечание: на C++ 11 вам не требуется макрос для 'static_assert', он встроен. –

+0

Это необходимо, чтобы использовать это на нескольких платформах, но разделяемая память доступна только на определенной машине, поэтому код должен быть скомпилирован для этого типа машины: что байт-порядок и размеры базовых типов фиксированы (по крайней мере для данной битности в компиляторе). Я бы сказал, что чересчур чрезмерно. –

+0

Это действительно зависит. Как кто-то сказал. «Одна платформа», а затем через год «пожалуйста, переносите это на эти платформы». Я бы предпочел быть оборонительным и не должен был искать такие проблемы после того, как они уже давно забыты – gman

Смежные вопросы