Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Portable way to retrieve a int32_t passed to variadic function

7.16.1.1 2 describes va_arg as following (emphasis mine):

If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:

  • one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
  • one type is pointer to void and the other is a pointer to a character type.

Now to my understanding and it seems that 6.5.2.2 (function calls) does not contradict me, though I might be wrong, the default promotions are:

  • char to either int or unsigned (implementation specified)
  • signed char to int
  • unsigned char to unsigned
  • short to int
  • unsigned short to unsigned
  • float to double

This is all fine and dandy when you know the exact underlying types passed to the va_list (except for char, which AFAIK is impossible to retrieve portably because its signedness is implementation specified).

It gets more complicated when you're expecting types from <stdint.h> to be passed to your va_list.

  • int8_t and int16_t, deducting through logical limit observations, are guaranteed to be promoted or already be of type int. However it's very dubious to rely on my original "logical" limit observations, so I'm seeking your (and the standard's) confirmation on this deduction (I may be missing some corner cases I'm not even aware of).
  • the same holds for uint8_t and uint16_t, except the underlying type is unsigned
  • int32_t may or may not be promoted to int. It may be larger than , smaller than or exactly the same as int. Same holds for uint32_t but for unsigned. How to portably retrieve int32_t and uint32_t passed to va_list? In other words, how to determine if int32_t (uint32_t) has been promoted to int(unsigned)? In yet other words, how to determine whether I should use va_arg(va, int) or va_arg(va, int32_t) to retrieve int32_t passed to the variadic function without invoking undefined behaviour on any platform?
  • I believe the same questions are valid for int64_t and uint64_t.

This is a theoretical (standard-only concerned) question, with a presumption that all exact-width types in <stdint.h> are present. I'm not interested in "what's true in practice" type of answers, because I believe I already know them.

EDIT

One idea that I have in mind is to use _Generic to determine the underlying type of int32_t. I'm not sure how exactly would you use it though. I'm looking for better (easier) solutions.

like image 444
MarkWeston Avatar asked Dec 28 '17 13:12

MarkWeston


1 Answers

#define IS_INT_OR_PROMOTED(X) _Generic((X)0 + (X)0, int: 1, default: 0)

Usage:

int32_t x = IS_INT_OR_PROMOTED(int32_t) ? 
              (int32_t)va_arg(list, int) : 
              va_arg(list, int32_t);

With gcc on my PC the macro returns 1 for int8_t, int16_t and int32_t, and 0 for int64_t.

With gcc-avr (a 16-bit target) the macro returns 1 for int8_t and int16_t, and 0 for int32_t and int64_t.

For long the macro returns 0 regardless of whether sizeof(int)==sizeof(long).

I don't have any targets with 64-bit ints but I don't see why it wouldn't work on such a target.

I'm not sure this will work with truly pathological implementations though Actually I'm pretty sure now it will work with any conforming implementation.

like image 138
n. 1.8e9-where's-my-share m. Avatar answered Oct 02 '22 13:10

n. 1.8e9-where's-my-share m.