Кросс-платформенный формат строки для переменных типа size_t?

В проекте кросс-платформы c/С++ (Win32, Linux, OSX) мне нужно использовать функции * printf для печати некоторых переменных типа size_t. В некоторых средах size_t составляет 8 байт, а на других - 4. На glibc у меня есть% zd, а на Win32 я могу использовать % Id. Есть ли элегантный способ справиться с этим?

Ответ 1

Макрос PRIuPTR (from < inttypes.h > ) определяет десятичный формат для uintptr_t, который всегда должен быть достаточно большим, чтобы вы могли отличать его size_t без усечения, например

fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);

Ответ 2

Здесь есть два вопроса. Первый вопрос - правильная строка спецификатора printf для трех платформ. Обратите внимание, что size_t является неподписанным типом.

В Windows используйте "%Iu".

В Linux и OSX, используйте "%zu".

Второй вопрос заключается в том, как поддерживать несколько платформ, учитывая, что на каждой платформе могут быть разные строки форматирования. Как отмечали другие люди, использование #ifdef становится уродливым быстро.

Вместо этого напишите отдельный файл makefile или project для каждой целевой платформы. Затем обратитесь к спецификатору с помощью некоторого имени макроса в исходных файлах, соответствующим образом определяя макрос в каждом make файле. В частности, как GCC, так и Visual Studio принимают переключатель "D" для определения макросов в командной строке.

Если ваша система сборки очень сложная (несколько вариантов сборки, сгенерированные источники и т.д.), поддержка 3 отдельных make файлов может стать необоснованной, и вам придется использовать какую-то усовершенствованную систему сборки, такую ​​как CMake или GNU autotools, Но основной принцип тот же: используйте систему сборки для определения макросов, специфичных для платформы, вместо того, чтобы размещать логику обнаружения платформы в ваших исходных файлах.

Ответ 3

Единственное, что я могу придумать, это типичное:

#ifdef __WIN32__ // or whatever
#define SSIZET_FMT "%ld"
#else
#define SSIZET_FMT "%zd"
#endif

а затем воспользовавшись постоянным складыванием:

fprintf(stream, "Your size_t var has value " SSIZET_FMT ".", your_var);

Ответ 4

Дэн Сакс написал статью в Embedded Systems Design, которая охватывала этот вопрос. По словам Дэна,% zu является стандартным способом, но немногие компиляторы поддержали это. В качестве альтернативы он рекомендовал использовать% lu вместе с явным приведением аргумента в unsigned long:

size_t n;
...
printf("%lu", (unsigned long)n);

Ответ 5

Используйте boost::format. Это типично, поэтому он будет правильно печатать size_t с помощью %d, также вам не нужно будет помещать c_str() на std::string при его использовании, и даже если вы передадите номер на %s или наоборот, это сработает.

Ответ 6

Я не знаю ни одного удовлетворяющего решения, но вы можете рассмотреть специализированную функцию для форматирования элементов size_t для строки и напечатать строку.

(В качестве альтернативы, если вы можете уйти от него, boost:: format справляется с таким типом с легкостью.)

Ответ 7

Вам просто нужно найти целочисленный тип с наибольшим классом хранения, указать значение для него и затем использовать соответствующую строку формата для более крупного типа. Обратите внимание, что это решение будет работать для любого типа (ptrdiff_t и т.д.), А не только size_t.

То, что вы хотите использовать, - uintmax_t и макрос формата PRIuMAX. Для Visual С++ вам понадобится загрузить c99-совместимые заголовки stdint.h и inttypes.h, поскольку Microsoft не предоставляет их.

Также см.

http://www.embedded.com/columns/technicalinsights/204700432

В этой статье исправлены ошибки в статье, цитируемой Фредерико.

Ответ 8

Мой выбор для этой проблемы состоит в том, чтобы просто привести аргумент size_t к unsigned long и использовать% lu всюду - это, конечно, только там, где значения не должны превышать 2 ^ 32-1. Если это слишком коротко для вас, вы всегда можете использовать unsigned long long и отформатировать его как% llu.

В любом случае, ваши строки никогда не будут неудобными.

Ответ 9

Опция 1:

Поскольку в большинстве (если не во всех?) PRIuPTR строка формата printf PRIuPTR из также достаточно длинный, чтобы содержать тип size_t, я рекомендую использовать следующие определения для строк формата size_t printf.

Тем не менее, важно убедиться, что это будет работать для вашего конкретного оборудования, так как стандарт не обеспечивает это.

#include <inttypes.h>

// Printf format strings for 'size_t' variable types.
#define PRIdSZT PRIdPTR
#define PRIiSZT PRIiPTR
#define PRIoSZT PRIoPTR
#define PRIuSZT PRIuPTR
#define PRIxSZT PRIxPTR
#define PRIXSZT PRIXPTR

Вариант 2:

Однако, где это возможно, просто используйте спецификатор длины %zu "z", как показано здесь, для типов size_t:

enter image description here

Однако в некоторых системах, таких как микроконтроллеры STM32, использующие в качестве компилятора gcc, спецификатор длины %z не обязательно реализован и выполняет что-то вроде printf("%zu\n", my_size_t_num); может просто закончить выводом литерала "% zu" (я лично проверил это и обнаружил, что это правда) вместо значения вашей переменной size_t.

Вариант 3:

Однако там, где вам нужно, чтобы это было абсолютно гарантировано, или если вы не уверены в своей конкретной архитектуре, просто uint64_t и распечатайте как uint64_t и все будет сделано, поскольку это гарантированно сработает, но требует дополнительного шага приведения., Пример:

#include <stdint.h>
#include <inttypes.h> 

size_t my_variable;
printf("%" PRIu64 "\n", (uint64_t)my_variable);

Источники цитируются:

  1. http://www.cplusplus.com/reference/cstdio/printf/
  2. http://www.cplusplus.com/reference/cinttypes/
  3. http://www.cplusplus.com/reference/cstdint/

Ответ 10

size_t является неподписанным типом по меньшей мере 16 бит. Часто видны ширины 32 и 64.

printf("%zu\n", some_size_t_object); // Standard since C99

Выше - это лучший способ продвижения вперед, но если код должен также переноситься на платформы pre-C99, скрывайте значение до некоторого широкого типа. unsigned long является разумным кандидатом, но может отсутствовать.

// OK, yet insufficient with large sizes > ULONG_MAX
printf("%lu\n", (unsigned long) some_size_t_object); 

или с условным кодом

#ifdef ULLONG_MAX
  printf("%llu\n", (unsigned long long) some_size_t_object); 
#else
  printf("%lu\n", (unsigned long) some_size_t_object); 
#endif

Наконец, рассмотрим double. Он немного неэффективен, но должен обрабатывать все древние и новые платформы до 2030-2040 годов, рассматривая закон Мура, когда double может не иметь точного результата.

printf("%.0f\n", (double) some_size_t_object);