Чистый код для printf size_t в C ++ (или: ближайший эквивалент C99% z в C ++) - PullRequest
94 голосов
/ 10 октября 2009

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

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

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

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

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

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

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

Есть идеи?


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

Ответы [ 8 ]

65 голосов
/ 03 ноября 2009

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

60 голосов
/ 03 ноября 2009

Большинство компиляторов имеют свой собственный спецификатор для аргументов 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);
17 голосов
/ 21 июня 2014

C ++ 11

C ++ 11 импортирует C99, поэтому 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));
16 голосов
/ 25 февраля 2011

На окнах и в реализации Visual Studio printf

 %Iu

у меня работает. увидеть * 1005 MSDN *

10 голосов
/ 10 октября 2009

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

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

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

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

7 голосов
/ 10 октября 2009

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

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 ) );
3 голосов
/ 27 октября 2015

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

#include "format.h"

size_t a = 42;

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

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

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

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

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

3 голосов
/ 10 октября 2009

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

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

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

...