Looking for tips for portable printf() specifiers for signed ints ofvarying sizes

S

Stephan Beal

Hi, all!

i am working on a piece of software where the size of one of the core
int types is configurable to be 8, 16, 32, or 64 bits (uint8_t ..
uint64_t). That's all working fine and well, but i've got a slight
problem when it comes to outputing them: my printf() specifiers have
to be different depending on the exact width of the type. (Granted,
all of that is debugging code, and doesn't directly affect the
library, but it annoys the hell out of me nonetheless.)

e.g.:

typedef uint32_t id_type;
....
id_type x = 34;
printf("%u\n", x );

that'll work, it seems, as long as i'm on a 32-bit platform. If i
reconfigure the software to use:

typedef uint64_t id_type;

then i've got a problem: i've got to change the printf() to use to
%llu or %lu (depends on the bitness of the platform, apparently) to
avoid a warning from the compiler (a reasonable warning, IMO, and not
one i want to ignore).

Is there a canon solution to dealing with this?

A related point: what are the proper printf specifiers to use for each
uint type (8-64 bits)? As best as i can determine they are:

uint8_t: %hhu
uint16_t: %hu
uint32_t: %u
uint64_t: %llu or %lu?

:-?

i seem to remember reading that %hhX and %llX are GNU-specific (or not
standard), but i can't find anything to back that up with at the
moment.

Are there *standard* specifiers i can reliably use for each of those
types?
 
F

Falcon Kirtaran

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Stephan said:
Hi, all!

i am working on a piece of software where the size of one of the core
int types is configurable to be 8, 16, 32, or 64 bits (uint8_t ..
uint64_t). That's all working fine and well, but i've got a slight
problem when it comes to outputing them: my printf() specifiers have
to be different depending on the exact width of the type. (Granted,
all of that is debugging code, and doesn't directly affect the
library, but it annoys the hell out of me nonetheless.)

e.g.:

typedef uint32_t id_type;
...
id_type x = 34;
printf("%u\n", x );

that'll work, it seems, as long as i'm on a 32-bit platform. If i
reconfigure the software to use:

typedef uint64_t id_type;

then i've got a problem: i've got to change the printf() to use to
%llu or %lu (depends on the bitness of the platform, apparently) to
avoid a warning from the compiler (a reasonable warning, IMO, and not
one i want to ignore).

Is there a canon solution to dealing with this?

A related point: what are the proper printf specifiers to use for each
uint type (8-64 bits)? As best as i can determine they are:

uint8_t: %hhu
uint16_t: %hu
uint32_t: %u
uint64_t: %llu or %lu?

:-?

i seem to remember reading that %hhX and %llX are GNU-specific (or not
standard), but i can't find anything to back that up with at the
moment.

Are there *standard* specifiers i can reliably use for each of those
types?

%llu (and %Lu, which is equivalent when using gnu) aren't standard
because long long unsigned int is not a standard type, if memory serves.

Just to clarify, what do you mean by core int types? Also, do you know
how they are configured? If it is a macro, I don't know if you can do
too much. If it is configured at runtime, you would need to use runtime
type identification to generate your format strings.

- --
- --Falcon Darkstar Kirtaran
- --
- --OpenPGP: (7902:4457) 9282:A431

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iQIcBAEBAgAGBQJJam0OAAoJEKmxP9YxEE4rWoYQAMMLkf0E567iJKsLomPxgLcT
BZuMPrZ+v09pqFoeT8t/NQ57v3+wInj96baRM9HrdMc+KRVdUoouIc5kT/GOh69U
ddVPOXLMa4ox6p4PRAL2B5x7NkXfzKbJJhy+8qyfluDFiaU5Gd2OxR81Ip7nd2nE
+vu46QadGgMlE7b6aR64/34LyycZA2wrbtGi0u6WAgMEV4o4c9onQgLbOC7n6GZ3
JUUMLfhcTOmHXbdcSUP6OWmku2WWHyBtNjtuwk9xZF6ibfzOgpKUnkpnQaUofvG5
eI1EOcssmnDn+exU+yIhoiYVQas0zE4SCRAzwVCDZi36pJiDqGUvBZAbAuIFLQbY
p52qxdaadId4v21Swwyiq+SO70WZyyyuJhwinT2HeTfLCZhI6FTaWpbDt0Q+3cUP
Xiwag8E0B++bkcK8i5iDVdPmNUpl60ND3qipMy4zMpzfjeYZIOnnhccuogaj+F9x
4Cmr+XjNh+8JgTN45V2yfEiQd+AxCFpo2k9xhzu6eGEA15Lkr9QVVN/cp0gDtpSZ
RHLAzlPy2V/9YpylBxPUsAefMTL+CeGNP/3GZfIMXJhGrsfeSWnDTsX9WRq1Z8al
gRytZFlRqFPqMgSH9rTpFZbRsOxA5oKOhTRiKH0n3JaZaIZNZ8dtVcdElJEhFwlK
tKvUscsn7z+BVJpMAuNb
=chMA
-----END PGP SIGNATURE-----
 
H

Harald van Dijk

Stephan said:
Hi, all!

i am working on a piece of software where the size of one of the core
int types is configurable to be 8, 16, 32, or 64 bits (uint8_t ..
uint64_t). That's all working fine and well, but i've got a slight
problem when it comes to outputing them: my printf() specifiers have to
be different depending on the exact width of the type. (Granted, all of
that is debugging code, and doesn't directly affect the library, but it
annoys the hell out of me nonetheless.)

Take a look at said:
e.g.:

typedef uint32_t id_type;
...
id_type x = 34;
printf("%u\n", x );

printf("%" PRIu32 "\n", x);
that'll work, it seems, as long as i'm on a 32-bit platform. If i
reconfigure the software to use:

typedef uint64_t id_type;

then i've got a problem: i've got to change the printf() to use to %llu
or %lu (depends on the bitness of the platform, apparently) to avoid a
warning from the compiler (a reasonable warning, IMO, and not one i
want to ignore).

printf("%" PRIu64 "\n", x);
Is there a canon solution to dealing with this?

#define PRIuID PRIu64

Define this near id_type, so that it is obvious you need to update this
when you update id_type's definition. Use this macro when printing.
A related point: what are the proper printf specifiers to use for each
uint type (8-64 bits)? As best as i can determine they are:

uint8_t: %hhu
uint16_t: %hu
uint32_t: %u
uint64_t: %llu or %lu?

:-?

i seem to remember reading that %hhX and %llX are GNU-specific (or not
standard), but i can't find anything to back that up with at the
moment.

%hhu is for unsigned char.
%hu is for unsigned short.
%u is for unsigned int.
%lu is for unsigned long int.
%llu is for unsigned long long int.

All of the above are standard C format specifiers, although some have
only become standard as of 1999 (and are less widely supported), but none
of them should be used for uintN_t types. PRIuN (where N is 8, 16, etc.)
are for uintN_t types.
 
S

Stephan Beal

%llu (and %Lu, which is equivalent when using gnu) aren't standard
because long long unsigned int is not a standard type, if memory serves.

i thought long long is C99 standard (but of course not supported
everywhere). Doesn't that imply the standardization of unsigned long
long as well?
Just to clarify, what do you mean by core int types?

8-64-bit ints
 Also, do you know
how they are configured?  If it is a macro, I don't know if you can do
too much.  If it is configured at runtime, you would need to use runtime
type identification to generate your format strings.

It's a bit of an odd beast, really. A macro defines the bitedness
(8/16/32/64). Then we #if/#else a typedef based on that bitedness. The
code is:

/* ... 50(!) lines of docs snipped ... */
#define WHEFS_ID_TYPE_BITS 16

#if WHEFS_ID_TYPE_BITS == 8
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 8-bit IDs"
typedef uint8_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 16
/* the most realistic value, IMO. */
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 16-bit IDs"
typedef uint16_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 32
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 32-bit IDs"
typedef uint32_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 64
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 64-bit IDs"
typedef uint64_t whefs_id_type;
#else
# error "WHEFS_ID_TYPE_BITS must be one of: 16, 32, 64"
#endif


i need a macro (i'd prefer an enum value) for WHEFS_ID_TYPE_BITS
because small parts of the code need to be conditionally compiled
based on the width of the type. i also must use types of known widths.
e.g. i can't rely on size_t because it's not the same size on all
platforms (i found out the hard way). Thus i'm using the <stdint.h>
uint8_t .. uint64_t. Signed numbers are invalid and of no concern for
my use case.
 
I

Ian Collins

Stephan said:
i thought long long is C99 standard (but of course not supported
everywhere). Doesn't that imply the standardization of unsigned long
long as well?
It does, both long long and unsigned long long are standard.
It's a bit of an odd beast, really. A macro defines the bitedness
(8/16/32/64). Then we #if/#else a typedef based on that bitedness. The
code is:

/* ... 50(!) lines of docs snipped ... */
#define WHEFS_ID_TYPE_BITS 16

#if WHEFS_ID_TYPE_BITS == 8
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 8-bit IDs"

Here would be the pace to define the appropriate (see Harald's post)
conversion specifier.
 
F

Falcon Kirtaran

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ian said:
It does, both long long and unsigned long long are standard.

Hmm. I most probably stand corrected.
Here would be the pace to define the appropriate (see Harald's post)
conversion specifier.

- --
- --Falcon Darkstar Kirtaran
- --
- --OpenPGP: (7902:4457) 9282:A431

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iQIcBAEBAgAGBQJJangoAAoJEKmxP9YxEE4rveEP+wQ9Ke28sUSRvP8ZYXCQkXtQ
cGxQONq9VGRbkO2zIp3GRwDZdPjHDBdpCPLie73dKnNRUTBJGchhRMvqHF7BLguW
HjxvZ62MXr6H6gx8iRttGFX7wDl0bm3IjBU6eWfUpZ3Dx8jAyjhkhMq9dlVLSFjT
4w/sNMdYkONk0KilHdZaeE1+J1QrwiwODkuq+KOCPJ1klgLX557yeMXiiWcbcCHA
uH5P/rkKXG3dAe9bWTgregphTH3uj7lmswIFllFuBcYntzVXGxUAxkfs7ueRcqPv
J9DHulcCt0MwdmZinE0h5ctAKg0Of/cknLKfzM46ecQK2KQ+DqQAXNiVFeAT2kqy
V18K0MfqKIOzijVA0M/nk+qRi+6HfVHZMI7yCtiNg/KG9FIx6T9F8f4e0k1DLUYC
CeostybG+xu22lwb9Kingh0wy0gSk9g6MpH2LCvr+BdDbsAPCmhBdEMk+4CXjD2P
tKo8R+JgB4zlVAha+04cD5AIeJb0hDtnkgUdGtPKh8DKIr7HfcvSeQB5tNPm4SMt
vyWpvlVi7ZzXbFeu06s0d0TE9FoEdPKfruDUHkNVMA3B+ILUdm+VrwYwQycwPi+o
PY/F53DjzKuzzvtYk85xWNGUV6zyesgo4fKzko4nIUgpY37fTvQ/odjC7GCQOJjv
tfpBf8kORkSW9NywrUK0
=zUiP
-----END PGP SIGNATURE-----
 
B

Barry Schwarz

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1



%llu (and %Lu, which is equivalent when using gnu) aren't standard
because long long unsigned int is not a standard type, if memory serves.

It is in C99 (section 6.2.5-4).
 
B

Barry Schwarz

Hi, all!

i am working on a piece of software where the size of one of the core
int types is configurable to be 8, 16, 32, or 64 bits (uint8_t ..
uint64_t). That's all working fine and well, but i've got a slight
problem when it comes to outputing them: my printf() specifiers have
to be different depending on the exact width of the type. (Granted,
all of that is debugging code, and doesn't directly affect the
library, but it annoys the hell out of me nonetheless.)

One simple solution would be to cast the arguments in you calls to
printf to the maximum size you compiler will support (presumably
uint64_t) and always use the format PRIu64 defined in inttypes.h. As
you say, it is only in the debugging code. If your system doesn't
provide PRIu64, you could probably achieve the same affect with either
unsigned long long or unsigned long and llu or lu.
e.g.:

typedef uint32_t id_type;

Is this not a standard type provided by your system in stdint.h
...
id_type x = 34;
printf("%u\n", x );

that'll work, it seems, as long as i'm on a 32-bit platform. If i
reconfigure the software to use:

typedef uint64_t id_type;

then i've got a problem: i've got to change the printf() to use to
%llu or %lu (depends on the bitness of the platform, apparently) to
avoid a warning from the compiler (a reasonable warning, IMO, and not
one i want to ignore).

Is there a canon solution to dealing with this?

A related point: what are the proper printf specifiers to use for each
uint type (8-64 bits)? As best as i can determine they are:

uint8_t: %hhu
uint16_t: %hu
uint32_t: %u
uint64_t: %llu or %lu?

Check section 7.8 and header inttypes.h.
 
B

Ben Pfaff

Stephan Beal said:
typedef uint32_t id_type;
...
id_type x = 34;
printf("%u\n", x );

that'll work, it seems, as long as i'm on a 32-bit platform. If i
reconfigure the software to use:

typedef uint64_t id_type;

Use a macro:
#include <inttypes.h>
typedef uint32_t id_type;
#define PRINT_ID PRIu32

Then you can print it with:
id_type x = 34;
printf("%"PRINT_ID"\n", x);

And when you change to 64-bit, then:
#include <inttypes.h>
typedef uint64_t id_type;
#define PRINT_ID PRIu64
and the code to print is updated automatically.
 
K

Keith Thompson

Barry Schwarz said:
One simple solution would be to cast the arguments in you calls to
printf to the maximum size you compiler will support (presumably
uint64_t) and always use the format PRIu64 defined in inttypes.h. As
you say, it is only in the debugging code. If your system doesn't
provide PRIu64, you could probably achieve the same affect with either
unsigned long long or unsigned long and llu or lu.
[...]

uintmax_t is going to be the largest unsigned type in any conforming
C99 implementation; <inttypes.h> provides conversion specifiers for
it. It's very likely to be the same as uint64_t, but an
implementation could provide a larger type -- or might provide, say,
uint80_t but not uint64_t. uintmax_t is guaranteed to exist.

In a conforming C90 implementation, unsigned long is the largest
unsigned type.

The tricky part is supporting pre-C99 implementations that support
long long, and perhaps even implementations that support long long but
not "%llu" (particularly if the compiler and runtime library are from
different suppliers).
 
S

Stephan Beal

Thanks again to all of your for your insights and clarifications -
this has been really helpful. :)

i've settled on essentially what several of you suggested:

#if WHEFS_ID_TYPE_BITS == 8
/* for very, very limited filesystems. There's lots of room for
overflows here! */
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 8-bit IDs"
# define WHEFS_ID_PRINTF_SPEC PRIu8
typedef uint8_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 16
/* the most realistic value, IMO. */
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 16-bit IDs"
# define WHEFS_ID_PRINTF_SPEC PRIu16
typedef uint16_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 32
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 32-bit IDs"
# define WHEFS_ID_PRINTF_SPEC PRIu32
typedef uint32_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 64
# define WHEFS_ID_PRINTF_SPEC PRIu16
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 64-bit IDs"
typedef uint64_t whefs_id_type;
#else
# error "WHEFS_ID_TYPE_BITS must be one of: 8, 16, 32, 64"
#endif
 
K

Keith Thompson

Stephan Beal said:
Thanks again to all of your for your insights and clarifications -
this has been really helpful. :)

i've settled on essentially what several of you suggested:

#if WHEFS_ID_TYPE_BITS == 8
/* for very, very limited filesystems. There's lots of room for
overflows here! */
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 8-bit IDs"
# define WHEFS_ID_PRINTF_SPEC PRIu8
typedef uint8_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 16
/* the most realistic value, IMO. */
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 16-bit IDs"
# define WHEFS_ID_PRINTF_SPEC PRIu16
typedef uint16_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 32
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 32-bit IDs"
# define WHEFS_ID_PRINTF_SPEC PRIu32
typedef uint32_t whefs_id_type;
#elif WHEFS_ID_TYPE_BITS == 64
# define WHEFS_ID_PRINTF_SPEC PRIu16
# define WHEFS_MAGIC_STRING "whefs version 20090109 with 64-bit IDs"
typedef uint64_t whefs_id_type;
#else
# error "WHEFS_ID_TYPE_BITS must be one of: 8, 16, 32, 64"
#endif

Note that this depends on the presence and correctness of the
<inttypes.h> header. Since this is new in C99, some implementations
might not yet support it. In particular, I've heard that Microsoft
has shown very little interest in supporting C99, but I don't know
the details.

Another alternative is to use a format for a very large type and
convert all your whefs_id_type arguments to that type when passing
them to printf:

whefs_id_type foo = <whatever>;
printf("foo = %lu\n", (unsigned long)foo);
 
R

Richard Bos

....and so on...
Note that this depends on the presence and correctness of the
<inttypes.h> header. Since this is new in C99, some implementations
might not yet support it.

OTOH, I would expect any reasonable implementation to have both uint8_t
and PRIu8, or neither, but not one without the other...
In particular, I've heard that Microsoft has shown very little interest
in supporting C99, but I don't know the details.

....OTGH, some software companies simply aren't reasonable.

Richard
 
F

Falcon Kirtaran

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Richard said:
...and so on...


OTOH, I would expect any reasonable implementation to have both uint8_t
and PRIu8, or neither, but not one without the other...


...OTGH, some software companies simply aren't reasonable.

Richard

If memory serves most of the new things present in C99 are implemented
in microsoft's tripe as proprietary extensions.

- --
- --Falcon Darkstar Kirtaran
- --
- --OpenPGP: (7902:4457) 9282:A431

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iQIcBAEBAgAGBQJJb4ueAAoJEKmxP9YxEE4rzP4QAKUH32e5fY7W7fhmwjN6Ra4T
J47jwAwdbsDWQyG2HOWu5uK4UNck//NRWDFrFLmr73SNUpZzKdqWf8DiTWzfepi+
HHWW4y4PN3JfchJykxuV8I/w+PWbuFCEpvdsiVGp8Md4vIi6P26mRYRyM8N4CMQL
L+W3Vu57SV5Q0s1IF5YsvkTgeELTMbdn5kAVip+PiRwYJfxG6PRXmuKcCy5N/3A9
QovsFA095JJT6GklsABCrQmZerHltyPA873kwaPpsZ2FbTZZRbkN8W89UWamA9n5
XKOY0J42+d/01H7/tAspn56XDABH6JRJi2WjshL1PrJtIN2M4uePEhVQrUe1P6kt
aarKNZVpnxXwofnpd1KTwDNF+a3THj3BKTtbhmZ3SAC5J6d99FZwUuOF/nfc3jzR
QgXcgAbFDuUF40cpfGjqDibEfGpVOhWAYktCvAvJmxbH/cu2lKAa1giqQijsI693
C5Ts70xuYx7GW/UHlzBSTlpP4hC9k9Yodx3CS3RgQqkdIRysuZuYbItOQlib7dly
M2/RwXdQ5aWoV1WkWBCMylRyQlj7dFtfwjJmpxrVSiRphdBNuJy5Y16riyRp0m0I
8gphNsC5gygIe/8YKLfWp+DWdKi6nX45xsO4bHj+u3BOt/gRYoPc1oPOYiosPjyD
yGrj18Tot8/aAaPKaV26
=VRmI
-----END PGP SIGNATURE-----
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,240
Members
46,830
Latest member
HeleneMull

Latest Threads

Top