So, I want to make a function(-like macro) that takes any number of arguments of different types and does something to it. I mean, I did manage to make it work, but I'm looking for a more elegant solution (or to make sure my way is the way it should look like).
Example code of a function macro print(...):
#ifndef EVIL_PRINT_H
#define EVIL_PRINT_H
#include <stdio.h>
#define TWENTY_SECOND_ARGUMENT(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, ...) _22
#define COUNT_ARGUMENTS(...) TWENTY_SECOND_ARGUMENT(__VA_ARGS__, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define PRINT_CHAR_EVIL(x) printf("%c", x)
#define PRINT_INT_EVIL(x) printf("%i", x)
#define PRINT_FLOAT_EVIL(x) printf("%f", x)
#define PRINT_DOUBLE_EVIL(x) printf("%d", x)
#define PRINT_PTR_EVIL(x) printf("%p", x)
#define PRINT_STR_EVIL(x) printf("%s", x)
#define PRINT_ONE_EVIL(x, ...) _Generic(x, \
char: PRINT_CHAR_EVIL(x), \
int: PRINT_INT_EVIL(x), \
float: PRINT_FLOAT_EVIL(x), \
double: PRINT_DOUBLE_EVIL(x), \
void *: PRINT_PTR_EVIL(x), \
char const *: PRINT_STR_EVIL(x), \
char *: PRINT_STR_EVIL(x) \
)
#define PRINT_TWO_EVIL(_1, _2, ...) PRINT_ONE_EVIL(_1); PRINT_ONE_EVIL(_2)
...
#define PRINT_TWENTY_ONE_EVIL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) PRINT_TWENTY_EVIL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20); PRINT_ONE_EVIL(_21)
#define print(...) do { \
switch (COUNT_ARGUMENTS(__VA_ARGS__)) { \
default:break; \
case 1: \
PRINT_ONE_EVIL(__VA_ARGS__); \
break; case 2: \
PRINT_TWO_EVIL(__VA_ARGS__, 2); \
... \
break; case 21: \
PRINT_TWENTY_ONE_EVIL(__VA_ARGS__, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21); \
} \
} while(0);
#endif
My problem with this approach is that it copies a lot of code for one call, but I don't know, maybe the compiler optimizes all unneeded branches out. Also one limitation is that it will not accept more than x (in my case - 21) number of arguments. Adding more arguments is no big deal, but the file size is going to grow, if you need like 100+ arguments.
Usage example:
#include "evil_print.h"
int main(void) {
struct {
int i;
char c;
} v = {.i = 100, .c = 'A'}, o;
o = v;
print(v.i, ", ", o.c);
}
Yes, I know that it is easy in c++, don't mention that language here.