views:

1627

answers:

8

On a cross platform c/c++ project (Win32, Linux, OSX), I need to use the *printf functions to print some variables of type size_t. In some environments size_t's are 8 bytes and on others they are 4. On glibc I have %zd, and on Win32 I can use %Id. Is there an elegant way to handle this?

+1  A: 

I don't know of any satisfying solution, but you might consider a specialized function to format size_t items to a string, and print the string.

(Alternatively, if you can get away with it, boost::format handles this kind of thing with ease.)

Head Geek
+3  A: 

The only thing I can think of, is the typical:

#ifdef __WIN32__ // or whatever
#define SSIZET_FMT "%ld"
#else
#define SSIZET_FMT "%zd"
#endif

and then taking advantage of constant folding:

fprintf(stream, "Your size_t var has value " SSIZET_FMT ".", your_var);
ΤΖΩΤΖΙΟΥ
Heh -- I was hoping it wouldn't come to this.
twk
Hopefully somebody else will provide something better…
ΤΖΩΤΖΙΟΥ
A: 

Can't you just test "sizeof(size_t)" to pick your format string?

Steve Fallows
+8  A: 

The PRIuPTR macro (from <inttypes.h>) defines a decimal format for uintptr_t, which should always be large enough that you can cast a size_t to it without truncating, e.g.

fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);
finnw
finnw, you better change your PRIuPTR into `PRIuPTR` and prefix your "fprintf" line with 4 spaces, so that they become formatted as code and there won't be a confusion between PRIuPTR and PRluPTR (as seen here).
ΤΖΩΤΖΙΟΥ
@ΤΖΩΤΖΙΟY, I added the prefix, but what do the backticks around PRIuPTR do? I can't see any difference.
finnw
Backticks let you put code (or other stuff you don't want formatted by markdown) inline with text. The markdown formatter often gets confused by things like underscores in identifiers. Backticking inline code helps with this.
Michael Burr
Backticks don't seem to work in comments.Don't know why. Also no preview in comments.
Rhythmic Fistman
Backticks also allow the reader to see the difference between Int and lnt :)
ΤΖΩΤΖΙΟΥ
+1  A: 

Dan Saks wrote an article in Embedded Systems Design which covered this matter. According to Dan, %zu is the standard way, but few compilers supported this. As an alternative, he recommended using %lu together with an explicit cast of the argument to unsigned long:

size_t n;
...
printf("%lu", (unsigned long)n);
That's not so great on systems that use the LLP64 programming model, such as 64-bit Windows.
bk1e
%zu is a C99 invention. Those compilers are indeed rare. C++ doesn't have the problem to start with.
MSalters
%zu has nothing to do with the compiler and everything to do with the standard libraries...
plinth
+2  A: 

Use boost::format. It's typesafe, so it'll print size_t correctly with %d, also you don't need to remember to put c_str() on std::strings when using it, and even if you pass a number to %s or vice versa, it'll work.

Lev
boost::format is damn slow
pachanga
+2  A: 

There are really two questions here. The first question is what the correct printf specifier string for the three platforms is. Note that size_t is an unsigned type.

On Windows, use "%Iu".

msdn.microsoft.com/en-us/library/tcxf1dw6(VS.71).aspx

On Linux and OSX, use "%zu".

linux.die.net/man/3/printf developer.apple.com/documentation/Darwin/Reference/Manpages/man3/printf.3.html

The second question is how to support multiple platforms, given that things like format strings might be different on each platform. As other people have pointed out, using #ifdef gets ugly quickly.

Instead, write a separate makefile or project file for each target platform. Then refer to the specifier by some macro name in your source files, defining the macro appropriately in each makefile. In particular, both GCC and Visual Studio accept a 'D' switch to define macros on the command line.

If your build system is very complicated (multiple build options, generated sources, etc.), maintaining 3 separate makefiles might get unwieldly, and you are going to have to use some kind of advanced build system like CMake or the GNU autotools. But the basic principle is the same-- use the build system to define platform-specific macros instead of putting platform-detection logic in your source files.

p.s. Someone please replace the URLs with actual hyperlinks. Apparently 'new users can only post a maximum of one hyperlink'.

A: 

You just have to find an integer type with the largest storage class, cast the value to it, and then use the appropriate format string for the larger type. Note this solution will work for any type (ptrdiff_t, etc.), not just size_t.

What you want to use is uintmax_t and the format macro PRIuMAX. For Visual C++, you are going to need to download c99-compatible stdint.h and inttypes.h headers, because Microsoft doesn't provide them.

Also see

http://www.embedded.com/columns/technicalinsights/204700432

This article corrects the mistakes in the article quoted by Frederico.