size_t in inttypes.h

I

ImpalerCore

One of the pet peeves of mine for a long time is printing size_t
values, because C99 has the nice little 'z' modifier, but in the
compiler I use for embedded work, these C99 features is not
supported. In those situations, the recommendation is to cast the
size_t value to unsigned long, and use %lu. The issue is that there
are two different source styles for the two compiler environments, and
I'd like to make a single style work for both compiler standards. Of
course one could resort to use %lu and casting in both environments,
but I don't like it.

The two styles clash because I am writing a library for which I'd like
to maintain support for C90 and C99 compilers, and many of the
examples use size_t variables, either directly or indirectly through a
library function's return value or argument. I don't want to maintain
two separate examples just because the rules for printing a size_t
differ.

I currently have a wrapper for <stdint.h> and <inttypes.h> that I use
to define C90 versions of the intN_t and uintN_t types, as well as
their fprintf macro format specifiers like PRIu32. If one lacks a
native <inttypes.h> header file, one can still manually define them by
looking at <limits.h>.

\code snippet
.... stdint.h definitions above like ...
#define INT32_MAX (2147483647L)
....

#if defined(_MSC_VER) || defined(_WIN32)
# define PRId32 "I32d"
# define PRIi32 "I32i"
#else
# if (INT32_MAX == LONG_MAX)
# define PRId32 "ld"
# define PRIi32 "id"
# elif (INT32_MAX == INT_MAX)
# define PRId32 "d"
# define PRIi32 "i"
# elif (INT32_MAX == SHRT_MAX)
# define PRId32 "hd"
# define PRIi32 "hi"
# else
# error "Platform not supported"
# endif
#endif
\endcode

One way to provide a single viable method to format a size_t integer
is to create an <inttypes.h> compatible fprintf macro format
specifier, let's call it PRI[ouxX]SIZE. If C99 is present, one can
simply defer to use "z" as the leading modifier, but if it's not
present, one can again use <limits.h> to determine the correct
modifier to apply.

\code snippet
#if (defined(__STDC__) && defined(__STDC_VERSION__))
# if (__STDC__ && __STDC_VERSION__ >= 199901L)
# define PRIoSIZE "zo"
# define PRIuSIZE "zu"
# define PRIxSIZE "zx"
# define PRIXSIZE "zX"
# endif
#else
/* ULLONG_MAX is defined in my wrapper if 64-bit integer extensions
are detected, even if it's not a C99 compiler. */
# if defined(ULLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
# define PRIoSIZE "llo"
# define PRIuSIZE "llu"
# define PRIxSIZE "llx"
# define PRIXSIZE "llX"
# elif (SIZE_MAX == ULONG_MAX)
# define PRIoSIZE "lo"
# define PRIuSIZE "lu"
# define PRIxSIZE "lx"
# define PRIXSIZE "lX"
# elif (SIZE_MAX == UINT_MAX)
# define PRIoSIZE "o"
# define PRIuSIZE "u"
# define PRIxSIZE "x"
# define PRIXSIZE "X"
# elif (SIZE_MAX == USHRT_MAX)
# define PRIoSIZE "ho"
# define PRIuSIZE "hu"
# define PRIxSIZE "hx"
# define PRIXSIZE "hX"
# else
# error "Platform not supported"
# endif
#endif
\endcode

Then in theory, one can use the following to print a size_t value in
either C90 or C99.

\code snippet
#include <stdio.h>
#include "my_inttypes.h"

int main( void )
{
printf( "sizeof int = %" PRIuSIZE "\n", sizeof (int) );
return 0;
}
\endcode

The only kicker is that SIZE_MAX is not defined in C90, so one must
either rely on an auto-configuration script to detect SIZEOF_SIZE_T
that you can infer SIZE_MAX from, or worse have a human define it
manually. Definitions like ((size_t)-1) don't seem to work with the
preprocessor well.

I must say that I find the idea of PRIuSIZE and friends tempting, just
to have a uniform method of printing size_t values in library API
examples that work in C90 and C99, and hopefully C1X. I'm not
familiar with the history of the fprintf macro specifiers, so I wonder
why PRIuSIZE and friends were not included, when it's an obvious
candidate due to a lack of an exact modifier to a format specifier.
In my opinion, it's much easier to define a stdint.h/inttypes.h
wrapper for a C90 compiler and use PRIuSIZE than to create/modify a
printf implementation to accept %zu.

Even if I do like and choose to use my own PRIuSIZE creation in my
library examples, I imagine it's likely to get flak from it being
unidiomatic. Am I destined to cast my size_t values to (unsigned
long) forever?

Just for my curiosity's sake, any reason why stdint.h and inttypes.h
were separated in the first place? After looking at inttypes.h, it
doesn't really define any integer types as it's name implies, mostly
format specifiers. It seems logical to just graft inttypes.h into
stdint.h and put it all in one place.

Thanks for any feedback,
John D.
 
S

Seebs

The two styles clash because I am writing a library for which I'd like
to maintain support for C90 and C99 compilers, and many of the
examples use size_t variables, either directly or indirectly through a
library function's return value or argument. I don't want to maintain
two separate examples just because the rules for printing a size_t
differ.

Ugh! Yes.

I'm still mostly doing the (unsigned long) thing, but I am not sure this
is the right choice, it's just a habit.
Just for my curiosity's sake, any reason why stdint.h and inttypes.h
were separated in the first place? After looking at inttypes.h, it
doesn't really define any integer types as it's name implies, mostly
format specifiers. It seems logical to just graft inttypes.h into
stdint.h and put it all in one place.

I believe the answer is:

Historically, it was <inttypes.h> and it was a Unixism or something to
that effect. Then the idea occurred that <stdint.h> defining just the
types would provide functionality suitable for freestanding environments,
or something to that effect.

.... I was actually there when it happened, but it was over a decade ago
and my memory isn't good.

-s
 
K

Keith Thompson

ImpalerCore said:
Just for my curiosity's sake, any reason why stdint.h and inttypes.h
were separated in the first place? After looking at inttypes.h, it
doesn't really define any integer types as it's name implies, mostly
format specifiers. It seems logical to just graft inttypes.h into
stdint.h and put it all in one place.

C99 7.8p1:
The header <inttypes.h> includes the header <stdint.h> and
extends it with additional facilities provided by hosted
implementations

<inttypes.h> provides macros to be used with *printf() and *scanf(),
which are only required for hosted implementations. <stdint.h>
is required for both hosted and freestanding implementations.
 
I

ImpalerCore

Ugh!  Yes.

I'm still mostly doing the (unsigned long) thing, but I am not sure this
is the right choice, it's just a habit.

Could you see yourself using PRIuSIZE and friends if it were ever to
get standardized? There's pretty good precedent for other types that
lack exact fprintf modifiers. The only question is whether the
committee would see it as unnecessary redundancy since C99 already
defined a format modifier "z", which unfortunately suffers from
spurious support in the C community.

Best regards,
John D.
 
S

Seebs

Could you see yourself using PRIuSIZE and friends if it were ever to
get standardized? There's pretty good precedent for other types that
lack exact fprintf modifiers. The only question is whether the
committee would see it as unnecessary redundancy since C99 already
defined a format modifier "z", which unfortunately suffers from
spurious support in the C community.

I would probably not, just because I'd expect anyone who had it to already
handle %z. I just wish people had been better about supporting z. :)

-s
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top