Sunday, 15 January 2012

c - determine argument type from __VA_ARGS__ in compile time -



c - determine argument type from __VA_ARGS__ in compile time -

i wish determine types of parameters passed function using va_args in order route right handler, in compile time (and not within function va_args()).

by determine type mean need know if trace contains integers or has strings in well, wish in compile time.

for example:

#define trace_handler(type_branch) (invoke_ ## type_branch) #define type_args(args) ______//determine if arguments uint32________ #define trace_(formatstring,...) trace_handler(type_args(__va_args__))(__va_args__) #define trace(id,formatstring,...) trace_(formatstring,__va_args__)

any ideas?

thanks!

you can compile-time dispatch on type of look _generic operator. note part of main c language, not preprocessor macros.

class="lang-c prettyprint-override">int x = 0; _generic(x, int: invoke_int, float: invoke_float, double: invoke_double)(x); //calls invoke_int x

the look give first argument _generic used type, @ compile-time, select value inline (in case, function pass runtime variable).

_generic intended utilize single parameter, , consequence examples show how overload functions single argument. engage in hefty metaprogramming create deeply-nested _generic trees chew way through passed arguments, here's 1 much simpler possible way overload function multiple arguments of multiple types:

class="lang-c prettyprint-override">#include <stdlib.h> #include <stdio.h> // specialized definitions void overload_1(int a, int b, int c) { printf("all ints (%d, %d, %d)\n", a, b, c); } void overload_2(int a, char * b, int c) { printf("b string (%d, %s, %d)\n", a, b, c); } void overload_3(char * a, int b, char * c) { printf("a , c strings (%s, %d, %s)\n", a, b, c); } void static_error(int l) { printf("error overload on %d\n", l); exit(1); } // type indices enum arg_type { int = 0, char_p }; // id of specialization list of arg types static inline int get_overload_id(int ac, int av[]) { homecoming (ac == 3 && av[0] == int && av[1] == int && av[2] == int) ? 1 : (ac == 3 && av[0] == int && av[1] == char_p && av[2] == int) ? 2 : (ac == 3 && av[0] == char_p && av[1] == int && av[2] == char_p) ? 3 : -1 //error ; } // overloaded definition #define overload(...) overload_ex(get_overload_id(m_nargs(__va_args__), (int[]){ m_for_each(get_arg_type, __va_args__) }), __va_args__) #define overload_ex(getid, ...) \ ((getid == 1) ? overload_1(get_arg(0, int, __va_args__), get_arg(1, int, __va_args__), get_arg(2, int, __va_args__)) \ :(getid == 2) ? overload_2(get_arg(0, int, __va_args__), get_arg(1, char_p, __va_args__), get_arg(2, int, __va_args__)) \ :(getid == 3) ? overload_3(get_arg(0, char_p, __va_args__), get_arg(1, int, __va_args__), get_arg(2, char_p, __va_args__)) \ :static_error(__line__)) #define get_arg_type(a) _generic(((void)0, (a)), int: int, char*: char_p), #define get_arg(n, t, ...) get_arg_default_##t(m_get_elem(n, __va_args__,0,0,0,0,0,0,0,0,0,0,0,0,0)) #define get_arg_default_int(a) _generic((a), int: (a), default: 0) #define get_arg_default_char_p(a) _generic(((void)0, (a)), char*: (a), default: null) // metaprogramming utility macros (not straight related #define m_nargs(...) m_nargs_(__va_args__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define m_nargs_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, n, ...) n #define m_conc(a, b) m_conc_(a, b) #define m_conc_(a, b) a##b #define m_for_each(actn, ...) m_conc(m_for_each_, m_nargs(__va_args__)) (actn, __va_args__) #define m_for_each_0(actn, e) e #define m_for_each_1(actn, e) actn(e) #define m_for_each_2(actn, e, ...) actn(e) m_for_each_1(actn, __va_args__) #define m_for_each_3(actn, e, ...) actn(e) m_for_each_2(actn, __va_args__) #define m_for_each_4(actn, e, ...) actn(e) m_for_each_3(actn, __va_args__) #define m_for_each_5(actn, e, ...) actn(e) m_for_each_4(actn, __va_args__) #define m_get_elem(n, ...) m_conc(m_get_elem_, n)(__va_args__) #define m_get_elem_0(_0, ...) _0 #define m_get_elem_1(_0, _1, ...) _1 #define m_get_elem_2(_0, _1, _2, ...) _2 #define m_get_elem_3(_0, _1, _2, _3, ...) _3 #define m_get_elem_4(_0, _1, _2, _3, _4, ...) _4 #define m_get_elem_5(_0, _1, _2, _3, _4, _5, ...) _5 // (end of utility stuff) int main(void) { overload(1, 2, 3); // prints "all ints (1, 2, 3)" overload(1, "two", 3); // prints "b string (1, two, 3)" overload("one", 2, "three"); // prints "a , c strings (one, 2, three)" }

(m_nargs, m_for_each , m_get_elem utility macros... can extend them more arguments easily, aren't straight connected this.)

the way works build big ternary-operator conditional look contains all possible specializations function. utilize get_arg macro each argument passed specialization, take using _generic whether supply actual argument (if it's right type branch), or suitable default replacement (if wrong one, in case go unused). _generic mapped on arguments using m_for_each build "runtime" array of type-id integers. array, plus number of arguments, passed get_overload_id integer id of function want call, utilize controlling look in big ternary expression.

despite using runtime-level c constructs (a big ternary variations, dispatch function command it), doesn't have real runtime cost: since arguments dispatch function constant , static inline, gcc (and presumably other half-decent compiler) can inline , optimise out of unused branches of big ternary, leaving specialization want in generated assembly (you can compile gcc -s , see case). compile-time operation.

c macros compilation trace varargs

No comments:

Post a Comment