Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

OK, fine, but how, then, can the two problems I described be solved?


Well, what did you do before intmax_t was added to C99?

- POSIX defines that in_port_t is equal to uint16_t, so for that type there's no problem at all ;)

- Don't use printf/scanf for this, roll your own

- Refuse to print/read uid_t that are outside intmax_t range

- Use an autoconf test


My program was first written when intmax_t already existed; AFAICT, intmax_t was introduced in C99. I also need to parse and/or print uid_d, gid_t, and pid_t, so I guess that in the future I’ll have to implement my own integer parser and printer, like user “orlp” suggested elsethread.

Until then I guess I’ll have to do something like

  #if sizeof(pid_t) > sizeof(uintmax_t)
  #error Nope
  #endif
…repeated for all the types I need to parse and/or print.


You can't use sizeof in #if, but you can use C11 static_assert instead.

Also this doesn't just apply "in the future" -- integer types larger than intmax_t already exist, C23 just updates the standard to match reality.


You can do this:

  #if SIZEOF_PID_T > SIZEOF_UINTMAX_T
  ...
  #endif
Where you detect these sizes from the toolchain and deposit them as #define constants in some "config.h" header.

There are ways to detect sizes by compiling a source file to an object file, and then analyzing the object file (no execution), so things work under cross-compiling.

I've used a number of tricks over the years, and settled on this one:

https://www.kylheku.com/cgit/txr/tree/configure?h=txr-278#n1...

The basic idea is that we can take a value like sizeof(long), which is a constant, and using nothing but constant arithmetic, we can convert it to the characters " 8". These characters can be placed into a character array where they are delimited by some prefix that we can look for in the compiled object file with some common utilities.

This is quite durable in the sense that it can be reasonably expected to work through a wide variety of object formats.

In that test program, I have a structure with some character arrays. The larger character arrays hold the informative prefixes. The two-byte arrays hold decimal digits for sizes. Two digits go up to 99 bytes so we are safe for a number of years.

The DEC macro calculates the ASCII decimal value of its argument, expanding to a two-byte initializer for a character array:

  #define D(N, Z) ((N) ? (N) + '0' : Z)
  #define UD(S) D((S) / 10, ' ')
  #define LD(S) D((S) % 10, '0')
  #define DEC(S) { UD(S), LD(S) }
E.g.

  DEC(42) -> /* the equivalent of */ { '4', '2' }
But actually

  DEC(42) -> { UD(42), LD(42) }
          -> { D((42) / 10, ' '), D((42) % 10, '0') }
          -> { ((42) / 10) ? ((42 / 10)) + '0' : ' ',
               ((42) % 10) ? ((42 % 10)) + '0' : '0' }
The space instead of a leading zero is so that if we pull this into shell arithmetic, it isn't confused for an octal number.


That problem could be a really good application for a _Generic macro, which is another newish language feature in C.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: