-2

How do you write code to compile cross-platform without warnings. For example, I don't get warnings on x64 platform, but I do on ARM (raspberry PI):

warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 5 has type ‘size_t {aka unsigned int}

Needless to say I don't want to disable warnings.

More examples and scenarios:

warning: format ‘%lu’ expects argument of type ‘long unsigned int’, 
but argument 5 has type ‘uint64_t {aka long long unsigned int}’  

uint64_t Created;       // 8 bytes
time_t now = time(NULL);
"Current time: %li sec, %lu nanosecs", now, msg.Created

size_t is probably the highest offender:

Basic use of sizeof:

warning: format ‘%lu’ expects argument of type ‘long unsigned int’, 
but argument 4 has type ‘unsigned int’
tr_debug("pbJobs size: %lu", sizeof(pbJobs));

tr_debug is equivalent of printf for Mbed OS platform. Yes, I compile on Mbed OS and Linux.

Sergei G
  • 1,231
  • 2
  • 14
  • 25
  • 1
    By using proper format specifiers for the types used? It is specifically easy with standard types which you are using. – Eugene Sh. Oct 04 '17 at 16:39
  • Read the docs to find the correct format specifier for each standard type. – Baum mit Augen Oct 04 '17 at 16:40
  • 1
    Post the code that causes the waring and the types of its parameters. – chux - Reinstate Monica Oct 04 '17 at 16:49
  • 2
    Don't use `*printf()` with C++ and avoid the whole issue. – chux - Reinstate Monica Oct 04 '17 at 16:56
  • Can you use `std::cout` and avoid the issue? – Thomas Matthews Oct 04 '17 at 17:24
  • There is a lot of assumptions that people made here. 1st of all not everybody has access to all of C++. 2nd the reason for the question is that on cross-platform systems size_t structure is taken as an argument and thus I work with size_t data type. 3rd is I work with existing libraries that might be using sprintf. I just imported C code into CPP project. So, I really need a stanard sprintf support. On one system it is of one size and on another system it is of another type. There is no one specifier that fits them all. Some comments show lack of knowledge. – Sergei G Oct 04 '17 at 18:17
  • Daemons log, log messages use format strings. If printf or its other forms where not used in production it would not be in use. – Sergei G Oct 04 '17 at 18:23
  • "not everybody has access to all of C++." + "I really need a stanard sprintf support." --> It would have been clearer to tag this question C and not C++. Dual tagging confuses if a C _or_ C++ solution is needed or if a C _and_ C++ solution is needed. – chux - Reinstate Monica Oct 04 '17 at 19:40
  • `printf()` with `time_t` is **not** as simply as casting to a wide integer nor using a matching printf specifier (there is none). `time_t` is not even necessarily an integer. A good answer is too small for this comment space. IAC, I suspect a dupe exist on printing `time_t`.. – chux - Reinstate Monica Oct 04 '17 at 19:45

2 Answers2

2

For size_t, assuming you have a sufficiently modern C library, use %zu.

If you can't use the z modifier (some older libraries unfortunately don't support it), cast to a wide-enough known type when printing, and then use a width specifier appropriate to that type:

size_t sz = sizeof(whatever);
...
printf("%lu", (unsigned long)sz);

This works as long as you're never working with a size larger than 4 billion or so, i.e. that can fit in 32 bits. If you're on a system where size_t is 64 bits but long is 32, you've theoretically got the problem of a size which size_t can hold but %lu can't print. Whether this is a problem for you, and what to do if it is, is up to you. (The ideal solution, if your library supports it, is to go back to %zu, which is the preferred solution and doesn't have this problem in 32-bit, 64-bit, or any other sized environments. Or I guess you could use unsigned long long and %llu.)

Steve Summit
  • 29,350
  • 5
  • 43
  • 68
0

This particular warning can be avoided by using explicitly sized integers instead of the native data types.

#include <cstdint>

int8_t a = 15; //usually an alias for char
uint16_t b = 4980; //usually an alias for unsigned short
uint32_t c = 1234567890; //usually an alias for unsigned int or unsigned long
int64_t d = 908070605040302010ll; //usually an alias for long long

The trick with the sized integers is that if, for example, long were 32-bits on one platform, but 64 bits on another, any uses of long would be non-portable. But int64_t will always be 64-bits, or else it simply won't exist on the given platform.

In your case specifically, your code seems to be presuming that size_t will always be 64-bits, but there's no guarantee of that. So you should be using uint64_t instead, which guarantees that whatever underlying data type it ends up using, it will be 64 bits (and unsigned, which is the only guarantee associated with size_t).

Baum mit Augen
  • 46,177
  • 22
  • 136
  • 173
Xirema
  • 18,577
  • 4
  • 26
  • 60