GLib не обеспечивает размер типа guint64

В одном из своих прошлых постов я рассказывал, как компилировать приложения, использующую библиотеку GTK. Однако, как оказалось, входящая в ее состав библиотека GLib имеет одну неприятную особенность.

Как-то я писал небольшую программу с ее использованием. Она нормально компилировалась и работала. Однако при попытке скомпилировать ее с ключом -m32 (создать 32-битное приложение) я получил ошибку и несколько предупреждений:

Текст ошибки гласил: «размер массива _GStaticAssertCompileTimeAssertion_0 меньше нуля». Что это за массив такой? В моей программе его нет.

Из текста ошибки ясно, что она произошла в функции _GLIB_CHECKED_ADD_U64, которая объявлена в модуле gtypes.h. Ниже приводится исходный код этой функции:

static inline gboolean _GLIB_CHECKED_ADD_U64 (
                       guint64 *dest, guint64 a, guint64 b) {
  G_STATIC_ASSERT(sizeof (unsigned long long) == sizeof (guint64));
  return !__builtin_uaddll_overflow(a, b, 
                   (unsigned long long *) dest); }

Ошибку компиляции вызывает макрос G_STATIC_ASSERT. Он объявлен в заголовочном файле gmacros.h. Массив _GStaticAssertCompileTimeAssertion_0 является частью его реализации. В этом легко убедиться, изучив код макроса.

По сути G_STATIC_ASSERT является аналогом оператора static_assert в С++. Посмотрим, какое условие он проверяет, и почему оно не выполняется при компиляции под 32 бита.

sizeof (unsigned long long) == sizeof (guint64)

Здесь проверяется соответствие типов. К сожалению, я не нашел, где вводится тип guint64. Но для понимания причины происходящего нам достаточно той информации, что приведена в документации (доступна по ссылке). Там guint64 описывается как:

«An unsigned integer guaranteed to be 64-bits on all platforms»

или в моём переводе:

«Беззнаковое целое имеющее размер 64 бита на всех платформах».

Это условие и проверяется макросом в нашей функции. Вроде бы все логично и все правильно. Но там же, в документации, приводится и описание того, как вводится тип guint64:

typedef unsigned long guint64;

Получается, что guint64 — это аналог типа unsigned long, но сравнивается он с типом unsigned long long. Давайте разбираться с типом long. Сразу оговорюсь, стандарт С++ не уточняет размер типа, он говорит только о диапазоне значений, которые могут в нем храниться.

Как показывает мой опыт и информация в интернете, в системах Linux размер типа long эквивалентен размеру типа size_t. То есть, на 32-битных машинах он равен 32 битам, а на 64-битных — 64 битам. Поэтому и возникает ошибка.

Когда я компилирую 64-битное приложение, тип long (а значит и guint64) имеет размер 64 бита, его размер равен размеру типа long long, и компиляция проходит нормально. Когда же я компилирую 32-битное приложение, тип long имеет размер 32 бита, условие в макросе G_STATIC_ASSERT не выполняется и компиляция прерывается.

Таким образом получается, что GLib в действительности не обеспечивает размер типа guint64 равным 64 битам на «всех платформах». И это огорчает.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *