Clean code to printf size_t in C++ (or: Nearest equivalent of C99's %z in C++)
I have some C++ code that prints a size_t
:
size_t a;
printf("%lu", a);
I'd like this to compile 开发者_运维百科without warnings on both 32- and 64-bit architectures.
If this were C99, I could use printf("%z", a);
. But AFAICT %z
doesn't exist in any standard C++ dialect. So instead, I have to do
printf("%lu", (unsigned long) a);
which is really ugly.
If there's no facility for printing size_t
s built into the language, I wonder if it's possible to write a printf wrapper or somesuch such that will insert the appropriate casts on size_t
s so as to eliminate spurious compiler warnings while still maintaining the good ones.
Any ideas?
Edit To clarify why I'm using printf: I have a relatively large code base that I'm cleaning up. It uses printf wrappers to do things like "write a warning, log it to a file, and possibly exit the code with an error". I might be able to muster up enough C++-foo to do this with a cout wrapper, but I'd rather not change every warn() call in the program just to get rid of some compiler warnings.
The printf
format specifier %zu
will work fine on C++ systems; there is no need to make it more complicated.
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);
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));
On windows and the Visual Studio implementation of printf
%Iu
works for me. see msdn
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 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.
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 ) );
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.
#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);
精彩评论