typesafe printf hackery - Mailing list pgsql-hackers
From | Andres Freund |
---|---|
Subject | typesafe printf hackery |
Date | |
Msg-id | 20190730181845.jyyk4selyohagwnf@alap3.anarazel.de Whole thread Raw |
List | pgsql-hackers |
Hi, I've occasionally wished for a typesafe version of pg_printf() and other varargs functions. The compiler warnings are nice, but also far from complete. Here's a somewhat crazy hack/prototype for how printf could get actual argument types. I'm far from certain it's worth pursuing this further... Nor the contrary. Note that this requires removing the parentheses from VA_ARGS_NARGS_'s result (i.e. (N) -> N). To me those parens don't make much sense, we're pretty much guaranteed to only ever have a number there. With the following example e.g. myprintf("boring fmt", 1, 0.1, (char)'c', (void*)0, "crazy stuff"); myprintf("empty argument fmt"); yield format string "boring fmt", 5 args arg number 0 is of type int: 1 arg number 1 is of type double: 0.100000 arg number 2 is of type bool: true arg number 3 is of type void*: (nil) arg number 4 is of type char*: crazy stuff format string "empty argument fmt", 0 args which'd obviously allow for error checking inside myprintf. #include "c.h" // hack pg version out of the way #undef printf // FIXME: This doesn't correctly work with zero arguments #define VA_ARGS_EACH(wrap, ...) \ VA_ARGS_EACH_EXPAND(VA_ARGS_NARGS(__VA_ARGS__)) (wrap, __VA_ARGS__) #define VA_ARGS_EACH_EXPAND(count) VA_ARGS_EACH_EXPAND_REALLY(VA_ARGS_EACH_INT_, count) #define VA_ARGS_EACH_EXPAND_REALLY(prefix, count) prefix##count #define VA_ARGS_EACH_INT_1(wrap, el1) wrap(el1) #define VA_ARGS_EACH_INT_2(wrap, el1, ...) wrap(el1), VA_ARGS_EACH_INT_1(wrap, __VA_ARGS__) #define VA_ARGS_EACH_INT_3(wrap, el1, ...) wrap(el1), VA_ARGS_EACH_INT_2(wrap, __VA_ARGS__) #define VA_ARGS_EACH_INT_4(wrap, el1, ...) wrap(el1), VA_ARGS_EACH_INT_3(wrap, __VA_ARGS__) #define VA_ARGS_EACH_INT_5(wrap, el1, ...) wrap(el1), VA_ARGS_EACH_INT_4(wrap, __VA_ARGS__) #define VA_ARGS_EACH_INT_6(wrap, el1, ...) wrap(el1), VA_ARGS_EACH_INT_5(wrap, __VA_ARGS__) #define VA_ARGS_EACH_INT_7(wrap, el1, ...) wrap(el1), VA_ARGS_EACH_INT_6(wrap, __VA_ARGS__) typedef enum printf_arg_type { PRINTF_ARG_BOOL, PRINTF_ARG_CHAR, PRINTF_ARG_INT, PRINTF_ARG_DOUBLE, PRINTF_ARG_CHARP, PRINTF_ARG_VOIDP, } printf_arg_type; typedef struct arginfo { printf_arg_type tp; } arginfo; // hackfix empty argument case #define myprintf(...) myprintf_wrap(__VA_ARGS__, "dummy") #define myprintf_wrap(fmt, ... ) \ myprintf_impl(fmt, VA_ARGS_NARGS(__VA_ARGS__) - 1, ((arginfo[]){ VA_ARGS_EACH(blurttype, __VA_ARGS__) }), __VA_ARGS__) // FIXME: Obviously not enough types #define blurttype(x) ((arginfo){_Generic(x, char: PRINTF_ARG_BOOL, int: PRINTF_ARG_INT, double: PRINTF_ARG_DOUBLE, char *:PRINTF_ARG_CHARP, void *: PRINTF_ARG_VOIDP)}) static const char* printf_arg_typename(printf_arg_type tp) { switch (tp) { case PRINTF_ARG_BOOL: return "bool"; case PRINTF_ARG_CHAR: return "char"; case PRINTF_ARG_INT: return "int"; case PRINTF_ARG_DOUBLE: return "double"; case PRINTF_ARG_CHARP: return "char*"; case PRINTF_ARG_VOIDP: return "void*"; } return ""; } static void myprintf_impl(char *fmt, size_t nargs, arginfo arg[], ...) { va_list args; va_start(args, arg); printf("format string \"%s\", %zu args\n", fmt, nargs); for (int argno = 0; argno < nargs; argno++) { printf("\targ number %d is of type %s: ", argno, printf_arg_typename(arg[argno].tp)); switch (arg[argno].tp) { case PRINTF_ARG_BOOL: printf("%s", ((bool) va_arg(args, int)) ? "true" : "false"); break; case PRINTF_ARG_CHAR: printf("%c", (char) va_arg(args, int)); break; case PRINTF_ARG_INT: printf("%d", va_arg(args, int)); break; case PRINTF_ARG_DOUBLE: printf("%f", va_arg(args, double)); break; case PRINTF_ARG_CHARP: printf("%s", va_arg(args, char *)); break; case PRINTF_ARG_VOIDP: printf("%p", va_arg(args, void *)); break; } printf("\n"); } } int main(int argc, char **argv) { myprintf("boring fmt", 1, 0.1, (char)'c', (void*)0, "crazy stuff"); myprintf("empty argument fmt"); } Greetings, Andres Freund
pgsql-hackers by date: