Typedefs и спецификаторы формата printf

Типичное использование typedefs состоит в том, чтобы позволить «типу» переменной передать лучшее представление о назначении переменной без переопределения стоящей за ней структуры хранения.

Однако я также рассматриваю typedef как способ изменить структуру хранения для класса переменных за один раз.

Например, если я определяю

typedef uint32_t my_offset_t

и иметь переменные типа my_offset_t, переключение кодовой базы с uint32_t на char или uint64_t так же просто, как изменение одной строки и перекомпиляция (при условии, что я использовал sizeof, а не жестко заданные размеры), за исключением случая из printf / scanf.

Есть ли способ простым способом поменять спецификаторы формата в соответствии с типом, без функций-оболочек вокруг printf/scanf, if-elses или ifdefs?

Спасибо!

Для всех, кто заинтересован, я модифицирую LKM, который использует 16-битные смещения, для работы с 32-битными смещениями, но хочу, чтобы он мог при необходимости переходить на 64-битные (или какие-то другие!) смещения с минимальными затратами. изменения.


person Vanwaril    schedule 09.05.2012    source источник


Ответы (3)


Это непростое дело, но <inttypes.h> из C99 и более поздних версий показывает, как это сделать.

Для каждого из определенных вами типов вам необходимо предоставить себе соответствующий набор макросов «PRI» и «SCN», стараясь избегать стандартизированного пространства имен.

Например, вы можете использовать XYZ в качестве префикса для конкретного проекта и создать:

XYZ_PRIx_my_offset_t
XYZ_PRId_my_offset_t
XYZ_PRIX_my_offset_t
XYZ_PRIu_my_offset_t
XYZ_PRIo_my_offset_t

и эквиваленты SCN. Кроме того, вы бы определили их с точки зрения эквивалентных макросов для базового типа, поэтому:

#define XYZ_PRIx_my_offset_t PRIx32
#define XYZ_PRId_my_offset_t PRId32
#define XYZ_PRIX_my_offset_t PRIX32
#define XYZ_PRIu_my_offset_t PRIu32
#define XYZ_PRIo_my_offset_t PRIo32

В своем коде вы строите строки формата с помощью макросов XYZ_PRIx_my_offset_t:

printf("Offset: 0x%.8" XYZ_PRIX_my_offset_t "\n", my_offset_value);

Если вам впоследствии нужно будет изменить все на 64-битное, вы соответствующим образом редактируете typedef и определения макросов, а остальная часть кода остается «неизменной». Если вы действительно осторожны, вы можете почти полностью измениться.

Убедитесь, что вы компилируете как в 32-битных, так и в 64-битных системах с большим количеством предупреждений. GCC не будет предупреждать о проблемах на вашей текущей платформе, но они могут появиться на другой. (Я только что исправил некоторый код, который был чистым на 64-битной версии, но нечистым на 32-битной версии; теперь он использует макрос типа XYZ_PRId_int4 вместо %d и корректно компилируется в обоих случаях.)

person Jonathan Leffler    schedule 09.05.2012

Если вы посмотрите на мой предыдущий вопрос о inttypes.h, вы увидите, как система определила формат спецификаторы могут использоваться вместе с typedefs (через #define) для создания пользовательских спецификаторов печати для ваших пользовательских типов.

person Ben Jackson    schedule 09.05.2012
comment
Спасибо за ответ :) Я принял другой, потому что это более полный ответ, на случай, если кто-то еще заинтересуется этим вопросом. - person Vanwaril; 09.05.2012

Другим решением является преобразование в intmax_t для подписанных типов и обратно в uintmax_t для беззнаковых типов. Например:

printf("%ju\n", (uintmax_t)n);

будет работать правильно, если n имеет любой беззнаковый тип.

Для функций *scanf() вам придется читать во временный объект, а затем присваивать.

(Это предполагает, что ваша библиотека времени выполнения поддерживает эти функции.)

person Keith Thompson    schedule 09.05.2012