Очистить код для 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);
Он был протестирован с основными компиляторами и обеспечивает согласованный вывод на разных платформах.
Отказ от ответственности. Я являюсь автором этой библиотеки.