Creazione di macro C con ## e __LINE__ (concatenazione di token con macro di posizionamento)


107

Voglio creare una macro C che crei una funzione con un nome basato sul numero di riga. Ho pensato di poter fare qualcosa del tipo (la funzione reale avrebbe dichiarazioni all'interno delle parentesi graffe):

#define UNIQUE static void Unique_##__LINE__(void) {}

Che speravo si espandesse a qualcosa come:

static void Unique_23(void) {}

Non funziona. Con la concatenazione di token, le macro di posizionamento vengono trattate letteralmente, finendo per espandersi a:

static void Unique___LINE__(void) {}

È possibile farlo?

(Sì, c'è una vera ragione per cui voglio farlo, non importa quanto possa sembrare inutile).


Penso che tu possa farlo funzionare con l'espansione macro indiretta .
Ben Stiglitz

4
possibile duplicato di Come concatenare due volte con il preprocessore C ed espandere una macro come in "arg ## _ ## MACRO"? Lo stesso vale per qualsiasi macro __LINE__(anche se questo è un caso d'uso comune.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Risposte:


176

Il problema è che quando si dispone di una sostituzione di macro, il preprocessore espande le macro in modo ricorsivo solo se non vengono applicati né l'operatore stringizing #né l' operatore token- paste ##. Quindi, devi usare alcuni livelli aggiuntivi di riferimento indiretto, puoi usare l'operatore di token-incolla con un argomento espanso ricorsivamente:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}

Poi, __LINE__viene ampliato il numero di linea durante l'espansione UNIQUE(poiché non è coinvolta sia con #o ##), e quindi l'incollatura gettone avviene durante l'espansione TOKENPASTE.

Va anche notato che c'è anche la __COUNTER__macro, che si espande a un nuovo intero ogni volta che viene valutata, nel caso in cui sia necessario avere più istanze della UNIQUEmacro sulla stessa riga. Nota: __COUNTER__è supportato da MS Visual Studio, GCC (dalla V4.3) e Clang, ma non è standard C.


3
Temo che non funzioni con GNU cpp. TOKENPASTE utilizza LINE come letterale. TOKENPASTE (Unique_, LINE ) si espande in Unique___LINE__
DD.

3
@DD: D'oh, risolto ora. Ha bisogno di 2 livelli di riferimento indiretto, non 1.
Adam Rosenfield,

La __COUNTER__macro non ha funzionato per me in gcc; anche se __LINE__quello ha funzionato come pubblicizzato.
Tyler

2
Un po 'di informazioni extra per chiunque provi COUNTER , secondo msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx è una macro specifica di Microsoft.
Elva

3
Qualche spiegazione del motivo per cui hai bisogno di 2 livelli di riferimento indiretto? L'ho provato con uno solo, assente di # e ##, e questo non lo espande su VS2017. Apparentemente lo stesso vale per GCC. Ma se aggiungi un 2 ° livello di riferimento indiretto, si espande. Magia?
Gabe Halsmer

-2

GCC non richiede "wrapping" (o realizzazione) a meno che il risultato non debba essere "stringificato". Gcc ha caratteristiche ma TUTTO può essere fatto con la semplice versione C 1 (e alcuni sostengono che Berkeley 4.3 C è molto più veloce che vale la pena imparare a usarlo).

** Clang (llvm) NON FA CORRETTAMENTE LO SPAZIO BIANCO per l'espansione macro - aggiunge spazi bianchi (che certamente distrugge il risultato come identificatore C per ulteriore pre-elaborazione) **, clang semplicemente non fa # o * espansione macro come preprocessore C dovrebbe per decenni. Il primo esempio è la compilazione di X11, la macro "Concat3" non funziona, il risultato è ora MISNAMED C Identifier, che ovviamente non riesce a compilare. e sto cominciando a costruire cose che falliscono sono la loro professione.

Penso che la risposta qui sia "il nuovo C che infrange gli standard è pessimo C", questi hack scelgono sempre di (spazi dei nomi clobber) cambiano i valori predefiniti senza motivo ma non "migliorano C" (tranne che per loro stessi lo dicono: che io diciamo che è un aggeggio fatto per spiegare perché se la cavano con tutti i guasti di cui nessuno li ha ancora resi responsabili).


Non è un problema che i precedenti pre-processori C non supportassero UNIq_ () __ perché supportavano #pragma che consente di contrassegnare come hackery "hacker di marca del compilatore nel codice" e funziona altrettanto bene SENZA influenzare gli standard: proprio come cambiare i valori predefiniti sono inutili rotture di wonton, e proprio come cambiare ciò che una funzione fa usando lo stesso nome (clobbering dello spazio dei nomi) è ... malware secondo me

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.