4 ms·
#define _H4X0R_CONVERT_ONE(arg) \ ((union { unsigned long long u; void *v; }){ \ .u = (unsigned long long)arg, \
by Joker_vD 1y ago
#define _H4X0R_CONVERT_ONE(arg) \
((union { unsigned long long u; void *v; }){ \
.u = (unsigned long long)arg, \
}).v
Couldn't this be just
#define _H4X0R_CONVERT_ONE(arg) (void*)(uintptr_t)(arg)
?
Also, thanks, now I can finally use
void my_printf(const char *fmt, void* args[], size_t argc);
ergonomically:
#define my_printf(fmt, ...) (my_printf)((fmt), \
(void*[]){ H4X0R_VA_VOID_STAR_CONVERT(__VA_ARGS__) }, \
H4X0R_VA_COUNT(__VA_ARGS__))
int main(int argc, char **argv) {
my_printf("int: %d, ptr: %p, str: %s, missing: %d\n", 42, argv, "Hello world!");
}
$ gcc test.c && ./a.out
int: 42, ptr: 0x7FFF46AA3E78, str: Hello world!, missing: %!d(MISSING)
Funnily enough, the difference between passing ... and locally-allocated void*[] is basically who has to spill the data to the stack, the caller or the called function.
- viega 1y agoWell, I've done it that way if I'm willing to limit myself to pointers or ints up to a pointer size, but that doesn't work with floats or doubles, for instance. Ergonomically, I have tended to start using _Generic for static type checking where possible, and that pushes me more toward to avoiding arrays in types for this kind of thing.