C & C ++ (risposta aggiornata)
Come osservato in un commento, la mia soluzione originale aveva due problemi:
- I parametri opzionali sono disponibili solo in C99 e negli standard successivi della famiglia linguistica.
- La virgola finale nella definizione dell'enum è anche specifica per C99 e successive.
Dato che volevo che il mio codice fosse il più generico possibile per funzionare su piattaforme più vecchie, ho deciso di provarlo ancora. È più lungo di prima, ma funziona su compilatori e preprocessori impostati sulla modalità di compatibilità C89 / C90. A tutte le macro viene passato un numero appropriato di argomenti nel codice sorgente, sebbene a volte quelle macro "si espandano" in nulla.
Visual C ++ 2013 (aka versione 12) emette avvisi sui parametri mancanti, ma né mcpp (un preprocessore open source che rivendica un'elevata conformità allo standard) né gcc 4.8.1 (con -std = iso9899: 1990 -pedantic-errori switch) emettono avvertimenti o errori per quelle invocazioni di macro con un elenco di argomenti effettivamente vuoto.
Dopo aver esaminato lo standard pertinente (ANSI / ISO 9899-1990, 6.8.3, sostituzione macro), ritengo che vi sia un'ambiguità sufficiente che questo non debba essere considerato non standard. "Il numero di argomenti in un'invocazione di una macro simile a una funzione deve corrispondere al numero di parametri nella definizione di macro ...". Non sembra precludere un elenco di argomenti vuoto fintanto che le parentesi necessarie (e le virgole nel caso di più parametri) sono in atto per invocare la macro
Per quanto riguarda il problema della virgola finale, che viene risolto aggiungendo un identificatore extra all'enumerazione (nel mio caso, MMMM che sembra ragionevole come qualsiasi cosa per l'identificatore seguire 3999 anche se non obbedisce alle regole accettate del sequenziamento numerico romano Esattamente).
Una soluzione leggermente più pulita implicherebbe lo spostamento dell'enum e il supporto delle macro in un file di intestazione separato, come era implicito in un commento altrove, e l'utilizzo di undef dei nomi delle macro subito dopo che sono stati utilizzati in modo da evitare l'inquinamento dello spazio dei nomi. Indubbiamente dovrebbero essere scelti anche nomi di macro migliori, ma questo è adeguato per l'attività da svolgere.
La mia soluzione aggiornata, seguita dalla mia soluzione originale:
#define _0(i,v,x)
#define _1(i,v,x) i
#define _2(i,v,x) i##i
#define _3(i,v,x) i##i##i
#define _4(i,v,x) i##v
#define _5(i,v,x) v
#define _6(i,v,x) v##i
#define _7(i,v,x) v##i##i
#define _8(i,v,x) v##i##i##i
#define _9(i,v,x) i##x
#define k(p,s) p##s,
#define j(p,s) k(p,s)
#define i(p) j(p,_0(I,V,X)) j(p,_1(I,V,X)) j(p,_2(I,V,X)) j(p,_3(I,V,X)) j(p,_4(I,V,X)) j(p,_5(I,V,X)) j(p,_6(I,V,X)) j(p,_7(I,V,X)) j(p,_8(I,V,X)) j(p,_9(I,V,X))
#define h(p,s) i(p##s)
#define g(p,s) h(p,s)
#define f(p) g(p,_0(X,L,C)) g(p,_1(X,L,C)) g(p,_2(X,L,C)) g(p,_3(X,L,C)) g(p,_4(X,L,C)) g(p,_5(X,L,C)) g(p,_6(X,L,C)) g(p,_7(X,L,C)) g(p,_8(X,L,C)) g(p,_9(X,L,C))
#define e(p,s) f(p##s)
#define d(p,s) e(p,s)
#define c(p) d(p,_0(C,D,M)) d(p,_1(C,D,M)) d(p,_2(C,D,M)) d(p,_3(C,D,M)) d(p,_4(C,D,M)) d(p,_5(C,D,M)) d(p,_6(C,D,M)) d(p,_7(C,D,M)) d(p,_8(C,D,M)) d(p,_9(C,D,M))
#define b(p) c(p)
#define a() b(_0(M,N,O)) b(_1(M,N,O)) b(_2(M,N,O)) b(_3(M,N,O))
enum { _ a() MMMM };
#include <stdio.h>
int main(int argc, char** argv)
{
printf("%d", MMMCMXCIX * MMMCMXCIX);
return 0;
}
La risposta originale (che ha ricevuto i primi sei voti, quindi se nessuno lo vota mai più, non dovresti pensare che la mia soluzione aggiornata abbia ottenuto i voti):
Nello stesso spirito di una risposta precedente, ma fatto in un modo che dovrebbe essere portatile usando solo comportamenti definiti (anche se ambienti diversi non sempre concordano su alcuni aspetti del preprocessore). Tratta alcuni parametri come facoltativi, ne ignora altri, dovrebbe funzionare su preprocessori che non supportano la __VA_ARGS__
macro, incluso C ++, utilizza macro indirette per garantire che i parametri siano espansi prima dell'incollaggio dei token, e infine è più breve e penso più facile da leggere ( anche se è ancora difficile e probabilmente non facile da leggere, solo più facile):
#define g(_,__) _, _##I, _##II, _##III, _##IV, _##V, _##VI, _##VII, _##VIII, _##IX,
#define f(_,__) g(_,)
#define e(_,__) f(_,) f(_##X,) f(_##XX,) f(_##XXX,) f(_##XL,) f(_##L,) f(_##LX,) f(_##LXX,) f(_##LXXX,) f(_##XC,)
#define d(_,__) e(_,)
#define c(_,__) d(_,) d(_##C,) d(_##CC,) d(_##CCC,) d(_##CD,) d(_##D,) d(_##DC,) d(_##DCC,) d(_##DCCC,) d(_##CM,)
#define b(_,__) c(_,)
#define a b(,) b(M,) b(MM,) b(MMM,)
enum { _ a };