La risposta di Leushenko è davvero interessante - solo: l' foo
esempio non si completa con GCC, che fallisce foo(7)
, inciampando sulla FIRST
macro e la chiamata effettiva della funzione ( (_1, __VA_ARGS__)
, rimanendo con una virgola in eccesso. Inoltre, siamo nei guai se vogliamo fornire sovraccarichi aggiuntivi , come ad esempio foo(double)
.
Così ho deciso di elaborare un po 'di più la risposta, anche per consentire un sovraccarico vuoto ( foo(void)
- che ha causato alcuni problemi ...).
L'idea ora è: definire più di un generico in diverse macro e lasciare selezionare quello corretto in base al numero di argomenti!
Il numero di argomenti è abbastanza semplice, basato su questa risposta :
#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)
#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y
È carino, risolviamo uno SELECT_1
o SELECT_2
(o più argomenti, se vuoi / ne hai bisogno), quindi abbiamo semplicemente bisogno di definizioni appropriate:
#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
int: foo_int, \
char: foo_char, \
double: foo_double \
)
#define SELECT_2(_1, _2) _Generic((_1), \
double: _Generic((_2), \
int: foo_double_int \
) \
)
OK, ho già aggiunto il sovraccarico del vuoto - tuttavia, questo in realtà non è coperto dallo standard C, che non consente argomenti variadici vuoti, quindi facciamo affidamento sulle estensioni del compilatore !
All'inizio, una chiamata a macroistruzione vuota ( foo()
) produce ancora un token, ma uno vuoto. Quindi la macro di conteggio in realtà restituisce 1 anziché 0 anche in caso di chiamata a macroistruzione vuota. Possiamo "facilmente" eliminare questo problema, se posizioniamo la virgola dopo in modo __VA_ARGS__
condizionale , a seconda che l'elenco sia vuoto o meno:
#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)
Che sembrava facile, ma la COMMA
macro è piuttosto pesante uno; fortunatamente, l'argomento è già trattato in un blog di Jens Gustedt (grazie, Jens). Il trucco di base è che le macro di funzioni non vengono espanse se non seguite da parentesi, per ulteriori spiegazioni, dai un'occhiata al blog di Jens ... Dobbiamo solo modificarle un po 'in base alle nostre esigenze (userò nomi più brevi e meno argomenti per brevità).
#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)
#define SET_COMMA(...) ,
#define COMMA(...) SELECT_COMMA \
( \
HAS_COMMA(__VA_ARGS__), \
HAS_COMMA(__VA_ARGS__ ()), \
HAS_COMMA(SET_COMMA __VA_ARGS__), \
HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)
#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3
#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,
E ora stiamo bene ...
Il codice completo in un blocco:
/*
* demo.c
*
* Created on: 2017-09-14
* Author: sboehler
*/
#include <stdio.h>
void foo_void(void)
{
puts("void");
}
void foo_int(int c)
{
printf("int: %d\n", c);
}
void foo_char(char c)
{
printf("char: %c\n", c);
}
void foo_double(double c)
{
printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
printf("double: %.2f, int: %d\n", c, d);
}
#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)
#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y
#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
int: foo_int, \
char: foo_char, \
double: foo_double \
)
#define SELECT_2(_1, _2) _Generic((_1), \
double: _Generic((_2), \
int: foo_double_int \
) \
)
#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N
#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)
#define SET_COMMA(...) ,
#define COMMA(...) SELECT_COMMA \
( \
HAS_COMMA(__VA_ARGS__), \
HAS_COMMA(__VA_ARGS__ ()), \
HAS_COMMA(SET_COMMA __VA_ARGS__), \
HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)
#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3
#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,
int main(int argc, char** argv)
{
foo();
foo(7);
foo(10.12);
foo(12.10, 7);
foo((char)'s');
return 0;
}