Очистить код для printf size_t в С++ (или: ближайший эквивалент C99% z в С++)

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

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

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

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

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

что действительно уродливо.

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

Любые идеи?


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

Ответ 1

Большинство компиляторов имеют свой собственный спецификатор для аргументов size_t и ptrdiff_t, Visual С++, например, используют% 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);

Ответ 2

Спецификатор формата printf %zu отлично работает на С++-системах; нет необходимости усложнять работу.

Ответ 3

В Windows и реализации Visual Studio printf

 %Iu

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

Ответ 4

С++ 11

С++ 11 импортирует C99, поэтому std::printf должен поддерживать спецификатор формата C99 %zu.

С++ 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));

Ответ 5

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

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

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

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

Ответ 6

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

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 ) );

Ответ 7

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

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

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

Ответ 8

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

#include "format.h"

size_t a = 42;

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

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

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

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

Отказ от ответственности. Я являюсь автором этой библиотеки.