清除 C + + 中的 printf size_t 代码(或者: 与 C + + 中的 C99的% z 最接近的等价物)

我有一些 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 wrappers 来完成诸如“写一个警告,将其记录到一个文件中,并可能以错误退出代码”之类的操作。我也许能够使用 cout 包装器集合足够的 C + +-foo 来完成这项工作,但是我不希望仅仅为了消除一些编译器警告而更改程序中的每一个 police ()调用。

78076 次浏览

Since you're using C++, why not use IOStreams? That should compile without warnings and do the right type-aware thing, as long as you're not using a brain-dead C++ implementation that doesn't define an operator << for size_t.

When the actual output has to be done with printf(), you can still combine it with IOStreams to get type-safe behavior:

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

It's not super-efficient, but your case above deals with file I/O, so that's your bottleneck, not this string formatting code.

The effective type underlying size_t is implementation dependent. C Standard defines it as the type returned by the sizeof operator; aside from being unsigned and a sort of integral type, the size_t can be pretty much anything which size can accommodate the biggest value expected to be returned by sizeof().

Consequently the format string to be used for a size_t may vary depending on the server. It should always have the "u", but may be l or d or maybe something else...

A trick could be to cast it to the biggest integral type on the machine, ensuring no loss in the conversion, and then using the format string associated with this known type.

here's a possible solution, but it's not quite a pretty one..

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

Most compilers have their own specifier for size_t and ptrdiff_t arguments, Visual C++ for instance use %Iu and %Id respectively, I think that gcc will allow you to use %zu and %zd.

You could create a macro:

#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

Usage:

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

The printf format specifier %zu will work fine on C++ systems; there is no need to make it more complicated.

On windows and the Visual Studio implementation of printf

 %Iu

works for me. see msdn

C++11

C++11 imports C99 so std::printf should support the C99 %zu format specifier.

C++98

On most platforms, size_t and uintptr_t are equivalent, in which case you can use the PRIuPTR macro defined in <cinttypes>:

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

If you really want to be safe, cast to uintmax_t and use PRIuMAX:

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

The fmt library provides a fast portable (and safe) implementation of printf including the z modifier for size_t:

#include "fmt/printf.h"


size_t a = 42;


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

In addition to that it supports Python-like format string syntax and captures type information so that you don't have to provide it manually:

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

It has been tested with major compilers and provides consistent output across platforms.

Disclaimer: I'm the author of this library.

#include <cstdio>
#include <string>
#include <type_traits>


namespace my{
template<typename ty>
auto get_string(ty&& arg){
using rty=typename::std::decay_t<::std::add_const_t<ty>>;
if constexpr(::std::is_same_v<char, rty>)
return ::std::string{1,arg};
else if constexpr(::std::is_same_v<bool, rty>)
return ::std::string(arg?"true":"false");
else if constexpr(::std::is_same_v<char const*, rty>)
return ::std::string{arg};
else if constexpr(::std::is_same_v<::std::string, rty>)
return ::std::forward<ty&&>(arg);
else
return ::std::to_string(arg);
};


template<typename T1, typename ... Args>
auto printf(T1&& a1, Args&&...arg){
auto str{(get_string(a1)+ ... + get_string(arg))};
return ::std::printf(str.c_str());
};
};

Later in code:

my::printf("test ", 1, '\t', 2.0);