Preprocessore standard C.
$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)
extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
extern void mine_3(char *x);
$
Due livelli di indiretta
In un commento ad un'altra risposta, Cade Roux ha chiesto perché questo abbia bisogno di due livelli di riferimento indiretto. La risposta impercettibile è perché è così che lo standard richiede che funzioni; tendi a scoprire che hai bisogno del trucco equivalente anche con l'operatore di stringere.
La sezione 6.10.3 della norma C99 riguarda la "sostituzione di macro" e la 6.10.3.1 riguarda la "sostituzione di argomenti".
Dopo aver identificato gli argomenti per l'invocazione di una macro simile a una funzione, ha luogo la sostituzione degli argomenti. Un parametro nell'elenco sostituzione, salvo preceduta da una #
o ##
pre-elaborazione gettone o seguita da una ##
preelaborazione gettone (vedi sotto), è sostituito dal parametro corrispondente dopo tutte le macro in essa contenute sono state espanse. Prima di essere sostituito, i token di preelaborazione di ogni argomento sono completamente sostituiti da macro come se formassero il resto del file di preelaborazione; non sono disponibili altri token di preelaborazione.
Nell'invocazione NAME(mine)
, l'argomento è "mio"; è completamente esteso a "mio"; viene quindi sostituito nella stringa di sostituzione:
EVALUATOR(mine, VARIABLE)
Ora viene scoperto il macro VALUTATORE e gli argomenti sono isolati come "mio" e "VARIABILE"; quest'ultimo viene quindi completamente espanso a "3" e sostituito nella stringa di sostituzione:
PASTER(mine, 3)
Il funzionamento di questo è coperto da altre regole (6.10.3.3 'L'operatore ##'):
Se, nell'elenco di sostituzione di una macro simile a una funzione, un parametro viene immediatamente preceduto o seguito da un ##
token di preelaborazione, il parametro viene sostituito dalla sequenza di token di preelaborazione dell'argomento corrispondente; [...]
Per invocazioni di macro sia simili a oggetti sia simili a funzioni, prima che l'elenco di sostituzione venga riesaminato per la sostituzione di più nomi di macro, ogni istanza di un ##
token di preelaborazione nell'elenco di sostituzione (non da un argomento) viene eliminata e il token di preelaborazione precedente viene concatenato con il seguente token di preelaborazione.
Quindi, l'elenco di sostituzione contiene x
seguito ##
e ##
seguito anche da y
; quindi abbiamo:
mine ## _ ## 3
ed eliminando i ##
token e concatenando i token su entrambi i lati combina "mio" con "_" e "3" per produrre:
mine_3
Questo è il risultato desiderato.
Se guardiamo alla domanda originale, il codice era (adattato per usare 'mio' invece di 'some_function'):
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
L'argomento di NAME è chiaramente "mio" e questo è completamente ampliato.
Seguendo le regole del 6.10.3.3, troviamo:
mine ## _ ## VARIABLE
che, quando gli ##
operatori vengono eliminati, esegue il mapping a:
mine_VARIABLE
esattamente come riportato nella domanda.
Preprocessore tradizionale C.
Robert Rüger chiede :
Esiste un modo per farlo con il preprocessore C tradizionale che non ha l'operatore di incollaggio token ##
?
Forse, e forse no - dipende dal preprocessore. Uno dei vantaggi del preprocessore standard è che ha questa funzione che funziona in modo affidabile, mentre c'erano diverse implementazioni per i preprocessori pre-standard. Un requisito è che quando il preprocessore sostituisce un commento, non genera uno spazio come il preprocessore ANSI è tenuto a fare. Il preprocessore GCC (6.3.0) C soddisfa questo requisito; il preprocessore Clang da XCode 8.2.1 no.
Quando funziona, questo fa il lavoro ( x-paste.c
):
#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
Nota che non c'è uno spazio tra fun,
e VARIABLE
- questo è importante perché, se presente, viene copiato nell'output e finisci con mine_ 3
il nome, che ovviamente non è sintatticamente valido. (Ora, per favore, posso riavere i miei capelli?)
Con GCC 6.3.0 (in esecuzione cpp -traditional x-paste.c
), ottengo:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_3(char *x);
Con Clang di XCode 8.2.1, ottengo:
# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2
extern void mine _ 3(char *x);
Quegli spazi rovinano tutto. Prendo atto che entrambi i preprocessori sono corretti; diversi preprocessori pre-standard presentavano entrambi i comportamenti, il che rendeva incollare token un processo estremamente fastidioso e inaffidabile quando si provava a eseguire il port code. Lo standard con la ##
notazione semplifica radicalmente questo.
Potrebbero esserci altri modi per farlo. Tuttavia, questo non funziona:
#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
GCC genera:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_VARIABLE(char *x);
Chiudi, ma senza dadi. YMMV, ovviamente, a seconda del preprocessore pre-standard che stai utilizzando. Francamente, se sei bloccato con un preprocessore che non collabora, probabilmente sarebbe più semplice disporre di un preprocessore C standard al posto di quello pre-standard (di solito c'è un modo per configurare il compilatore in modo appropriato) che passare molto tempo a cercare di trovare un modo per fare il lavoro.