tags:

views:

1138

answers:

5

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.

+3  A: 

Since you're using C++, why not use cout? 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.

EDIT: 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.

Warren Young
I know that Google forbids the use of cout in their code. Perhaps Justin L. is working under such a restriction.
Kinopiko
In my case (see edit above), an interesting idea might be to try and implement the warn() function in terms of cout. But that would involve parsing format strings manually, which is...tricky. :)
Justin L.
Edited to answer your edit.
Warren Young
Your latest edit is actually the opposite of what I think might work for me. I don't want to rewrite all of the code which invokes a printf wrapper, but I wouldn't mind rewriting the implementation of the printf wrapper to use cout. But I don't think that's going to happen. :)
Justin L.
+1  A: 

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.

mjv
I would be cool casting my `size_t`s to the largest integral type on the machine and using the format string associated with this type. My question is: Is there a way I can do this while maintaining clean code (warnings only for legitimate printf format string errors, no ugly casts, etc.)? I could write a wrapper which changes the format string, but then GCC wouldn't be able to give me warnings when I legitimately messed up my format string.
Justin L.
+3  A: 

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 tGet< unsigned long long > //or whatever the 64bit unsigned is called..
{
  static const char* id;
};

const char* tGet< unsigned long long >::id = "%lu";

//should be repeated for any type size_t can ever have


printf( GetPrintfID< size_t >::id, sizeof( x ) );
stijn
Well...that does achieve my goal of safety and no warnings. But...yeesh. I'll take the warnings if that's what I have to do. :)
Justin L.
+2  A: 

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)
  #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);
dalle
+3  A: 

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

Will
This works great. Thanks!
Justin L.