Clean code to printf size_t in C++ (or: Nearest equivalent of C99's %z in C++)

我的未来我决定 提交于 2019-11-27 00:07:54

问题


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_ts 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_ts 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.

回答1:


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



回答2:


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




回答3:


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



回答4:


On windows and the Visual Studio implementation of printf

 %Iu

works for me. see msdn




回答5:


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.




回答6:


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



回答7:


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.




回答8:


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

#include "format.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.



来源:https://stackoverflow.com/questions/1546789/clean-code-to-printf-size-t-in-c-or-nearest-equivalent-of-c99s-z-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!