108

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.
Justin L.
  • 3,957
  • 3
  • 31
  • 29
  • 4
    Why are you using printf at all should be the question. – Ed S. Oct 10 '09 at 01:41
  • does your compiler inspect the printf string and type check for you? – Pod Oct 10 '09 at 01:54
  • My compiler does indeed inspect the printf format string and type check it for me. I'd like to keep this feature turned on. – Justin L. Oct 10 '09 at 01:56
  • 2
    %zu, z is a width specifier not type specifier. It works for the c printf that you can use seamlessly from C++. I've commented on it below, so vote for it ;) – Will Nov 03 '09 at 18:31
  • If you're using Visual Studio, can't you just use `"%l"`? Won't that always be the right size? Or does portability matter? – Mooing Duck Jan 04 '13 at 20:11
  • C casts are ugly. You could use the constructor syntax like `int(a)` if you had an appropriate `typedef`. – Wolf Aug 02 '17 at 11:03

9 Answers9

75

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

Will
  • 73,905
  • 40
  • 169
  • 246
  • 9
    @ChrisMarkle A quick test shows me it doesn't work in MinGW. Also the MS site doesn't list it (http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx). I suppose the answer is no. – wump Sep 09 '12 at 17:56
62

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);
SergA
  • 1,097
  • 13
  • 21
dalle
  • 18,057
  • 5
  • 57
  • 81
  • 5
    It is not that easy. Whether `%z` is supported or not depends on the runtime, not the compiler. Using `__GNUC__` is therefore a bit of a problem, if you mix GCC/mingw with msvcrt (and without using mingw's augmented printf). – jørgensen Nov 15 '13 at 06:06
24

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));
Oktalist
  • 14,336
  • 3
  • 43
  • 63
16

On windows and the Visual Studio implementation of printf

 %Iu

works for me. see msdn

Iceland_jack
  • 6,848
  • 7
  • 37
  • 46
meissnersd
  • 1,272
  • 2
  • 12
  • 21
12

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.

Warren Young
  • 40,875
  • 8
  • 85
  • 101
  • I know that Google forbids the use of cout in their code. Perhaps Justin L. is working under such a restriction. –  Oct 10 '09 at 01:44
  • 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. Oct 10 '09 at 01:55
  • 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. Oct 11 '09 at 01:56
  • Use `std::stringstream` instead of IO streams. – Thomas Eding Jun 06 '13 at 18:27
  • 1
    Streams have awkward notation. Compare: `printf("x=%i, y=%i;\n", x, y);` vs `cout << "x=" << x << ", y=" << y << ";" << std::endl;`. – wonder.mice Nov 13 '15 at 19:10
  • @wonder.mice you can write a variadic-template wrapper which would take arguments separated by commas, it it makes it more readable for you. This will also save you from some headaches with operator precedence. See [an example](http://ideone.com/WQHi8o). In any case this is much more robust than relying on types defined in a format string. – Ruslan Jun 06 '17 at 11:04
  • @Ruslan Checkout https://github.com/fmtlib/fmt for a better alternative. Also modern compilers check types defined in format string, so no problems there. – wonder.mice Jun 06 '17 at 19:12
  • @wonder.mice I wouldn't call runtime detection of errors in format string "better". Yes, that library may have some merits, but such errors can and should be detected at compile time, as is with iostream. Exceptions or whatever — it's just a crutch. – Ruslan Jun 06 '17 at 19:24
9

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.

vitaut
  • 49,672
  • 25
  • 199
  • 336
7

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 ) );
stijn
  • 34,664
  • 13
  • 111
  • 163
  • 2
    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. Oct 11 '09 at 01:57
  • 1
    Not pretty?! Depends on taste. It allows to fully portable use of printf with beasts like uintptr_t and alike. Great! – Slava Jan 04 '13 at 14:35
  • @user877329 you could build that format string as an std::string and then append GetPrintfID< size_t >::id at the place you need it – stijn Mar 07 '14 at 16:25
  • @stijn In other words: no compile-time concatenation availible – user877329 Mar 08 '14 at 18:07
  • @user877329 no (unless using macros, or I'm missing something). But why would that be a hard requirement? – stijn Mar 08 '14 at 19:41
3

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
  • 73,152
  • 14
  • 113
  • 156
  • 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. Oct 10 '09 at 02:07
  • Use CPP macros to test for the size of types; go for the one that matches and specify the format string that goes with the matching type. – Clearer May 24 '14 at 21:18
0
#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);
SU3
  • 5,064
  • 3
  • 35
  • 66
Red.Wave
  • 2,790
  • 11
  • 17