Чистый код для печати размера t в C++ (или: ближайший эквивалент c99 %z в C++)

у меня есть код C++, который печатает size_t:

size_t a;
printf("%lu", a);

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

если бы это был C99, я мог бы использовать printf("%z", a);. Но AFAICT %z не существует ни в одном стандартном диалекте C++. Поэтому вместо этого я должен сделать

printf("%lu", (unsigned long) a);

это действительно некрасиво.

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

какие идеи?


редактировать чтобы уточнить, почему я использую printf: у меня относительно большая база кода, которую я очищаю. Он использует обертки printf, чтобы делать такие вещи, как"написать предупреждение, войти в файл и, возможно, выйти из кода с ошибкой". Возможно, я смогу собрать достаточно сил. C++ - foo, чтобы сделать это с оболочкой cout, но я бы предпочел не изменять каждый вызов warn() в программе, чтобы избавиться от некоторых предупреждений компилятора.

8 ответов


большинство компиляторов имеют свой собственный спецификатор для size_t и ptrdiff_t аргументы, например, Visual C++ используют %Iu и %Id соответственно, я думаю, что gcc позволит вам использовать %zu и %zd.

вы можете создать макрос:

#if defined(_MSC_VER) || defined(__MINGW32__) //__MINGW32__ should goes before __GNUC__
  #define JL_SIZE_T_SPECIFIER    "%Iu"
  #define JL_SSIZE_T_SPECIFIER   "%Id"
  #define JL_PTRDIFF_T_SPECIFIER "%Id"
#elif defined(__GNUC__)
  #define JL_SIZE_T_SPECIFIER    "%zu"
  #define JL_SSIZE_T_SPECIFIER   "%zd"
  #define JL_PTRDIFF_T_SPECIFIER "%zd"
#else
  // TODO figure out which to use.
  #if NUMBITS == 32
    #define JL_SIZE_T_SPECIFIER    something_unsigned
    #define JL_SSIZE_T_SPECIFIER   something_signed
    #define JL_PTRDIFF_T_SPECIFIER something_signed
  #else
    #define JL_SIZE_T_SPECIFIER    something_bigger_unsigned
    #define JL_SSIZE_T_SPECIFIER   something_bigger_signed
    #define JL_PTRDIFF_T_SPECIFIER something-bigger_signed
  #endif
#endif

использование:

size_t a;
printf(JL_SIZE_T_SPECIFIER, a);
printf("The size of a is " JL_SIZE_T_SPECIFIER " bytes", a);

на printf формата %zu будет отлично работать в системах c++; нет необходимости усложнять его.


в windows и реализации printf в Visual Studio

 %Iu

работает для меня. видеть в MSDN


C++11

C++11 импортирует C99 so std::printf должен поддерживать C99 %zu формат описателя.

C++98

на большинстве платформ, size_t и uintptr_t эквивалентны, в этом случае вы можете использовать PRIuPTR макрос определен в <cinttypes>:

size_t a = 42;
printf("If the answer is %" PRIuPTR " then what is the question?\n", a);

если вы действительно хотите быть в безопасности, литые uintmax_t и использовать PRIuMAX:

printf("If the answer is %" PRIuMAX " then what is the question?\n", static_cast<uintmax_t>(a));

поскольку вы используете C++, почему бы не использовать IOStreams? Это должно компилироваться без предупреждений и делать правильные вещи с учетом типа, пока вы не используете реализацию C++ с мертвым мозгом, которая не определяет operator << на size_t.

когда фактический выход должен быть сделан с printf(), вы все еще можете объединить его с IOStreams, чтобы получить типобезопасное поведение:

size_t foo = bar;
ostringstream os;
os << foo;
printf("%s", os.str().c_str());

это не суперэффективно, но ваш случай выше имеет дело с файловым вводом/выводом, так что это ваше узкое место, а не этот код форматирования строки.


вот возможное решение, но оно не совсем красивое..

template< class T >
struct GetPrintfID
{
  static const char* id;
};

template< class T >
const char* GetPrintfID< T >::id = "%u";


template<>
struct GetPrintfID< unsigned long long > //or whatever the 64bit unsigned is called..
{
  static const char* id;
};

const char* GetPrintfID< unsigned long long >::id = "%lu";

//should be repeated for any type size_t can ever have


printf( GetPrintfID< size_t >::id, sizeof( x ) );

эффективный тип, лежащий в основе size_t-это зависит от реализации. Стандарт C определяет его как тип, возвращаемый оператором sizeof; помимо того, что он беззнаковый и своего рода интегральный тип, size_t может быть практически любым, размер которого может вместить наибольшее значение, ожидаемое от sizeof().

следовательно, строка формата, используемая для size_t, может варьироваться в зависимости от сервера. Он всегда должен иметь "u", но может быть l или d или может что-то еще...

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


на Формат C++ библиотека обеспечивает быструю портативную (и безопасную) реализацию printf в том числе z модификатор для size_t:

#include "format.h"

size_t a = 42;

int main() {
  fmt::printf("%zu", a);
}

в дополнение к этому он поддерживает синтаксис строки формата Python-like и захватывает информацию о типе, так что вам не нужно предоставлять ее вручную:

fmt::print("{}", a);

Он был протестирован с основными компиляторами и обеспечивает последовательный выход на разных платформах.

отказ от ответственности: я автор этой библиотеки.