portable printf() spec for size_t, long long, and others

Bug #1740426 reported by mdavidsaver on 2017-12-28
8
This bug affects 1 person
Affects Status Importance Assigned to Milestone
EPICS Base
Wishlist
mdavidsaver

Bug Description

I've been in the habit of using '%lld' and '%zu' to print 'long long' and 'size_t' respectively. However, the windows world needs '%I64d' and '%Iu' instead. It would be nice to have compatibility macros for this.

stdint.h/inttypes.h has a convention for this for the standard fixed width types. eg. PRIx32 for 'uint32_t'. This is used like:

> printf("%"PRIx32"\n", (uint32_t)42);

http://en.cppreference.com/w/c/types/integer

Considering the fact that issues related to these constructs are popping up again and again I would also like to see this addressed.

Fixed-width integer types are provided by stdint.h since C99 (https://en.wikipedia.org/wiki/C_data_types#stdint.h). In the case of GCC it seems like the standard library ships with this feature since 4.5 (see https://gcc.gnu.org/c99status.html). MSVC includes the required macros in 2015+ (https://docs.microsoft.com/en-us/cpp/standard-library/cstdint?view=vs-2015), according to StackOverflow even since 2010 (see https://stackoverflow.com/questions/126279/c99-stdint-h-header-and-ms-visual-studio). Not sure about clang.

Considering the fact that the 7.0 branch needs to support VxWorks 6 with GCC 4.3 we can only implement this in 7.1 (assuming that we want to stay away from implementing/maintaining these macros in EPICS Base).

rivers (rivers) wrote :

> I've been in the habit of using '%lld' and '%zu' to print 'long long' and 'size_t' respectively.
> However, the windows world needs '%I64d' and '%Iu' instead.
> It would be nice to have compatibility macros for this.

I'm not sure I understand this comment. I have been using %lld and %llu on Windows. I believe I tested and %z is supported on VS2015 and up, but not on VS2010. But do we need to support VS2010 any more?

Mark, you're right, MSVC supports %z for size_t (which is certainly preferred over the Microsoft-specific %I). This is supported at least since VS 2015 (https://docs.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=vs-2015#size-prefixes-for-printf-and-wprintf-format-type-specifiers). But I'm not aware of a sane portable way of writing

int64_t i = 42;
printf("Works on Windows only: %I64d\n", i);

other than

printf("Works on all C99-compatible platforms: %"PRId64"\n", i);

Sure the following works

printf("Works on all C99-compatible platforms: %lli\n", (long long)i);

but I always have to pull up the standard to be sure the type I'm casting to has sufficient size on all platforms. And it also requires C99.

Andrew Johnson (anj) wrote :

The epics integer types from epicsTypes.h are the same on all architectures:

typedef char epicsInt8;
typedef unsigned char epicsUInt8;
typedef short epicsInt16;
typedef unsigned short epicsUInt16;
typedef int epicsInt32;
typedef unsigned int epicsUInt32;
typedef long long epicsInt64;
typedef unsigned long long epicsUInt64;

Because of the promotion rules these should use the unmodified printf() format specifiers %d/i/o/u/x except for the 64-bit types which need the %ll modifier, even when building for Windows. The Windows C runtime library has supported %lld since Windows versions after XP, but GCC has taken a while to catch up and may still be expecting %I64d so could generate printf-format warnings. We added -Wno-format to silence them for MinGW builds recently (after the 3.15.7 and 7.0.3.1 releases), but I just discovered that we can define __USE_MINGW_ANSI_STDIO instead which seems to be better (I will make that change).

Note that the stdint.h types such as int32_t might /not/ map the same way as the epics types depending on how the header defines them and whether long is 32 or 64-bits on that architecture — int32_t might be either int or long on ILP32 architectures, so GCC might expect %d or %ld for it. That's why I avoid using those types myself (and the fact that Wind River only added stdint.h in VxWorks 6.9).

The problem is therefore just size_t, which may need to be %lu or %zu depending on the architecture (I'm assuming all 64-bit arch's can handle %z). There is unfortunately no standard PRIuXXX macro for size_t, presumably because %z had already taken care of that.

If we define a macro PRIuSIZE or EPRIuSIZE in Base, most support modules wouldn't be able to rely on it since they generally have to build with older Base versions as well, so they'd end up having to do something like this too:

#ifndef EPRIuSIZE
# if defined(vxWorks)
# define EPRIuSIZE "lu"
# else
# define EPRIuSIZE "zu"
# endif
#endif

That ought to be sufficient for current architectures. but would need to be longer to support VS2010. If VxWorks 7 adds support for %z that difference would be handled in a newer version of Base anyway, so EPRIuSIZE would be correctly defined by that new Base already.

I find these printf() macros ugly and might not use this one myself, but I agree that we should probably provide and document it. Questions:

1. Which header should we define this in? The C99 standard macros are found in inttypes.h; epicsTypes.h is one possibility, although that has nothing to do with size_t. epicsStdio.h?

2. What name should we use? EPRIuSIZE, PRIuSIZE, PRIuS, ...? Do we need any non-'u' versions, say an 'x' version?

Did I miss anything?

> Because of the promotion rules these should use the unmodified printf()
> format specifiers %d/i/o/u/x except for the 64-bit types which need the
> %ll modifier, even when building for Windows.
Rather than making assumptions about the mapping to platform-dependent types we should leave these things up to the standard library. Also technically long long is part of C99/C++11.

> #ifndef EPRIuSIZE
> # if defined(vxWorks)
> # define EPRIuSIZE "lu"
> # else
> # define EPRIuSIZE "zu"
> # endif
> #endif
Can we safely assume that pre-C99/C++11 compilers are only used for VxWorks? To be safe, I would suggest to start using %zu with EPICS 7.1. If we do so no EPICS-specific macro is required.

Using %zu as well as the format-string macros for fixed-size integer types defined by inttypes.h seems consequent and the most portable solution to me (with EPICS 7.1). Should a new format specifier make it into the C/C++ standard we can safely replace the ugly macros.

All this is probably affecting support modules more than Base since fixed-size integer types and printf aren't used that heavily in Base.

To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers