C & C ++ (Güncellenmiş Cevap)
Bir yorumda görüldüğü gibi, orijinal çözümümün iki sorunu vardı:
- İsteğe bağlı parametreler yalnızca C99 ve daha sonra dil ailesinin standartlarında bulunur.
- Enum tanımındaki izleyen virgül C99 ve sonrasına da özgüdür.
Kodumun eski platformlarda çalışabilmesi için olabildiğince jenerik olmasını istediğim için, başka bir bıçak almaya karar verdim. Daha öncekinden daha uzun, ancak C89 / C90 uyumluluk moduna ayarlanmış derleyiciler ve önişleyiciler üzerinde çalışıyor. Tüm makrolar, kaynak kodda uygun sayıda argüman iletilir, ancak bu makrolar hiçbir zaman "genişler".
Visual C ++ 2013 (aka sürüm 12) eksik parametrelerle ilgili uyarılar yayınlar, ancak ne mcpp (standarda uygun olduğunu iddia eden açık kaynak kodlu bir işlemci) ne de gcc 4.8.1 (-std = iso9899: 1990 -pedantik-hata anahtarları) etkili bir boş argüman listesi olan bu makro çağrıları için uyarılar veya hatalar.
İlgili standardın gözden geçirilmesinden sonra (ANSI / ISO 9899-1990, 6.8.3, Makro Değiştirme), bunun standart dışı kabul edilmemesi için yeterli belirsizlik olduğunu düşünüyorum. "İşlev benzeri bir makro çağırmadaki argümanların sayısı, makro tanımındaki parametre sayısı ile aynı olacaktır ...". Makroyu çağırmak için gerekli parantezler (ve çoklu parametreler durumunda virgüller) bulunduğu sürece boş bir argüman listesi bulunmuyor gibi görünmektedir.
Sondaki virgül sorununa gelince, bu numaralandırmaya fazladan bir tanımlayıcı ekleyerek çözülür (benim durumumda, MMMM, Roma rakamlı sıralamanın kabul edilen kurallarına uymasa bile, tanımlayıcı için 3999 izlemesi gereken herhangi bir şey kadar makul görünüyor. kesinlikle).
Biraz daha temiz bir çözüm, enum ve destek makrolarının başka bir yerde yorumlandığı gibi ayrı bir başlık dosyasına taşınmasını ve ad alanını kirletmemek için kullanıldıktan hemen sonra makro adlarının undef kullanılmasını içerecektir. Daha iyi makro isimleri de şüphesiz seçilmelidir, ancak bu eldeki görev için yeterlidir.
Güncellenen çözümüm, ardından özgün çözümüm:
#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;
}
Orijinal cevap (ilk altı yükseltme oyunu aldı, bu yüzden kimse bunu bir daha yenemezse, güncellenmiş çözümümün en iyi değeri aldığını düşünmemelisin):
Daha önceki bir cevapla aynı ruhta, ancak yalnızca tanımlanmış davranış kullanılarak taşınabilir olması gereken bir şekilde yapılmasına rağmen (farklı ortamlar her zaman önişlemcinin bazı yönleri üzerinde hemfikir değildir). Bazı parametreleri isteğe bağlı olarak ele alır, diğerlerini yok sayar, __VA_ARGS__
makroyu desteklemeyen ön işlemciler üzerinde çalışmalıdır, C ++ dahil, dolaylı makroları kullanır ve parametrelerin token yapıştırılmasından önce genişlemesini sağlamak için dolaylı makrolar kullanır ve sonunda daha kısa olduğunu ve daha kolay okunacağını düşünüyorum ( yine de zor ve okunması kolay olmasa da, daha kolay):
#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 };