C ++ Makroları ile İsteğe Bağlı Parametreler


106

C ++ Makroları ile isteğe bağlı parametreleri almanın bir yolu var mı? Bir çeşit aşırı yükleme de hoş olurdu.



Belki de işlev aşırı yüklemeleri, varsayılan parametreler, değişken şablonlar veya muhtemelen adlandırılmış parametre deyimi aradığınız şeydir
smoothware

Lütfen seçmiş olduğunuz yanıtı, düşük oy alanlara değil, gerçek çözümlerle yüksek oy alanlara güncelleyinNo you can't
Albert Renshaw

Yanıtlar:


158

İşte bunu yapmanın bir yolu. İlk olarak yardımcı makronun adını oluşturmak ve ardından bağımsız değişkenleri bu yardımcı makroya iletmek için bağımsız değişkenler listesini iki kez kullanır. Bir makroya yönelik bağımsız değişkenlerin sayısını saymak için standart bir numara kullanır.

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

Bu, makroyu arayan için kolaylaştırır, ancak yazar için değil.


2
Bu oldukça havalı, ancak PRINT_STRING yapsaydım işe yarayacağını sanmıyorum. Bu durumda, varsayılan bir çıktı olmazdı (ve kullanmak istediğim durum bu). Gerçekten harika için hala +1.
Cenoc

2
benim için gcc'de çalışıyor (ve çok akıllıca!) :-) ama benim için Visual Studio'da çalışmıyor :-(
Tim Gradwell

4
@TimGradwell - MSVC derleyicisindeki, kabul ettikleri ancak yaklaşık on yıldır düzeltilmeyen bir hatadan kaynaklanıyor. Ancak, geçici çözümler mevcuttur .
BeeOnRope

Akıllıca, ancak "GET_4th_ARG" da devam eden 'dışarı itme' olayı nedeniyle isteğe bağlı değişken makro argümanlar için çalışmıyor.
searchengine27

bu PRINT_STRING_MACRO_CHOOSERgerekli mi? Direkt olarak iç gövdesiyle değiştirip tüm bunları ile çağırabilir miyim (__VA_ARGS__)?
Herrgott

85

Cevabı için Derek Ledbetter'a büyük bir saygı ve eski bir soruyu yeniden canlandırdığı için özür dileriz.

O yapıyor ve preceed yeteneğine başka yerde toplayıp ne bir anlayış alma __VA_ARGS__ile ##izin bana bir varyasyon ile gelip ...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

Yanıtı bulan, ancak nasıl çalıştığını tam olarak göremeyen benim gibi uzman olmayanlar için, aşağıdaki koddan başlayarak gerçek işleme adım atacağım ...

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

Oluyor ...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

Bu sadece altıncı argüman oluyor ...

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

Not: Derleme hatası almak için XXX_0 için #define'ı kaldırın [yani: bağımsız değişken yok seçeneğine izin verilmezse].

PPS: Geçersiz durumların (örneğin: 5) programcıya daha net bir derleme hatası veren bir şey olması güzel olurdu!

PPPS: Uzman değilim, bu yüzden yorumları (iyi, kötü veya diğer) duyduğuma çok sevindim!


3
MAKRO adı olması gereken seçili bağımsız değişkeni # (kare işareti) kullanarak dizeye dönüştürdüyseniz ve ilk n karakterini beklenen önekle karşılaştırırsanız ve eşleşme yoksa, bilgilendirici bir yazdırırsanız, net bir derleme hatası alabilirsiniz. hata.
AturSams

1
Vay canına, bunun işe yarayıp yaramadığını bilmiyorum ama en azından çok yaratıcı!
Sınırlı Kefaret

4
neden ilk argüman her zaman boştur? neden onu XXX_X(,##__VA_ARGS__,` ... atlayamıyoruz : XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); ``
rahman

2
Boş ilk argüman (virgül) önemlidir. ## __ VA_ARGS__ önünde virgül varsa – ## __ VA_ARGS__ hiçbir şeye genişlerse virgülü kaldırır. Bunu "Olurlar ..." örneğinde görebilirsiniz, çünkü ilk (bağımsız değişken yok) satırda yalnızca 6 parametre vardır, ancak geri kalanı 7 alır. Bu numara, hiçbir bağımsız değişken durumunun işe
yaramasını sağlar

@Eric - microsoft derleyicilerindeki bir hatadan kaynaklanıyor, ancak bu soruyu geçici çözümler için görebilirsiniz .
BeeOnRope

31

C ++ makroları C'den değişmedi. C, işlevler için aşırı yükleme ve varsayılan bağımsız değişkenlere sahip olmadığından, kesinlikle makrolar için bunlara sahip değildi. Sorunuzu yanıtlamak için: hayır, bu özellikler makrolar için mevcut değil. Tek seçeneğiniz, farklı adlara sahip birden çok makro tanımlamak (veya hiç makro kullanmamak).

Bir yan not olarak: C ++ 'da, makrolardan olabildiğince uzaklaşmak genellikle iyi bir uygulama olarak kabul edilir. Bunun gibi özelliklere ihtiyacınız varsa, makroları aşırı kullanma ihtimaliniz yüksektir.


4
Makroları "aşırı yüklemenin" imkansız olmasının nedeninin, içlerinde herhangi bir türlerinin olmaması olduğunu unutmayın. Makrolar basitçe genişletilir.
mk12

2
Ben mümkün olduğunca az Makro kullanan rağmen, iz çıkışı üzerinden ayıklama gibi şeylerle biraz daha kolay olur bulundu __FILE__ve __LINE__ve bu tür ...
Christian Severin

iyi bir cevap değil. bu iyi bir cevap: stackoverflow.com/q/27049491/893406
v.oddou

Koşullu Derleme ve Hata Ayıklama / Günlük, makroların gerçekten kullanışlı ve yasal olduğu etki alanıdır. Her ciddi programcı bunu bilir. Sabitleri tanımlamak için makro kullanmaktan ve konteyner şablonları oluşturmak için bazı çılgın C seviyesi kodlama işleri yapmaktan uzaklaşmak iyi bir uygulamadır. C ++ 'ın makrolara daha fazla özellik eklemesini diliyorum. Şablonlara ortogonaldirler. Elbette en iyisi, alana özel dil (yönler) için derleyiciye oluşturucular eklememe izin veren kod hücreleri olacaktır.
Lothar

1
Ayrıca bunun iyi bir cevap olmadığını düşünüyorum, çünkü bir makro, herhangi bir C ++ dil seçeneğinden tamamen farklı bir şeydir, çünkü derleyiciden ÖNCE ele alınacaktır. Yani başka şeyler de yapabilirsiniz ve hiçbir derleyici veya bağlayıcı kodu optimize etmemelidir, çünkü belki de optimize etmemelidir.
alabamajack

26

En büyük ilgili olarak Derek Ledbetter'a , David Sorkovsky , Syphorlate birlikte tarafından boş makro argümanları algılamak için ustaca yöntemle onların cevapları için, Jens Gustedt at

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

sonunda tüm püf noktaları içeren bir şey buldum, böylece çözüm

  1. İşlev aşırı yüklemesini sağlamak için yalnızca standart C99 makrolarını kullanır , GCC / CLANG / MSVC uzantısı dahil değildir (yani, , ##__VA_ARGS__GCC / CLANG için özel ifade tarafından virgül yutma ve ##__VA_ARGS__MSVC için örtük yutma ). Yani --std=c99dilerseniz eksikleri derleyicinize iletmekten çekinmeyin =)
  2. İçin Works sıfır argüman yanı sıra bağımsız değişkenler sınırsız sayıda , kendi ihtiyaçlarını karşılamak için daha da genişletmek eğer
  3. Makul çapraz platformda çalışır , en azından test edilmiştir

    • GNU / Linux + GCC (CentOS 7.0 x86_64 üzerinde GCC 4.9.2)
    • GNU / Linux + CLANG / LLVM , (CentOS 7.0 x86_64 üzerinde CLANG / LLVM 3.5.0)
    • OS X + Xcode , (OS X Yosemite 10.10.1 üzerinde XCode 6.1.1)
    • Windows + Visual Studio , (Windows 7 SP1 64 bit üzerinde Visual Studio 2013 Güncelleştirme 4)

Tembellik için, kaynağı kopyalamak için bu yazının en sonuna atlayın. Aşağıda, __VA_ARGS__benim gibi genel çözümleri arayan herkese yardımcı olmayı ve ilham vermeyi umduğumuz ayrıntılı açıklama yer almaktadır . =)

İşte nasıl gidiyor. İlk olarak adlandırılan, kullanıcı tarafından görülebilen aşırı "fonksiyon" tanımlar createve ilgili gerçek fonksiyon tanımlama realCreateve bağımsız değişkenler farklı sayıda makro tanımlar CREATE_2, CREATE_1, CREATE_0, aşağıda gösterildiği gibidir:

#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

MACRO_CHOOSER(__VA_ARGS__)Parçası sonuçta makro tanımı adları çözer ve ikinci (__VA_ARGS__)bölüm bunların parametre listelerini ihtiva etmektedir. Yani bir kullanıcının create(10)çözme çağrısı CREATE_1(10), CREATE_1parça geliyor MACRO_CHOOSER(__VA_ARGS__)ve (10)parça ikinciden geliyor (__VA_ARGS__).

MACRO_CHOOSEREğer bu numarayı kullanan __VA_ARGS__boştur, aşağıdaki ifade önişlemci tarafından geçerli bir makro çağrısı içine birleştirilmiş:

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

Ustaca, ortaya çıkan bu makro çağrısını şöyle tanımlayabiliriz:

#define NO_ARG_EXPANDER() ,,CREATE_0

İki virgülü not edin, yakında açıklanacaklar. Bir sonraki kullanışlı makro

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

yani çağrıları

create();
create(10);
create(20, 20);

aslında genişletildi

CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);

Makro adından da anlaşılacağı gibi, argümanların sayısını daha sonra sayacağız. İşte başka bir numara geliyor: önişlemci yalnızca basit metin değişimi yapıyor. Bir makro çağrısının argümanlarının sayısını yalnızca parantez içinde gördüğü virgül sayısından çıkarır. Virgülle ayrılmış gerçek "bağımsız değişkenler" geçerli sözdizimine sahip olmak zorunda değildir. Herhangi bir metin olabilirler. Yani, yukarıdaki örnekte, NO_ARG_EXPANDER 10 ()orta arama için 1 argüman olarak sayılır. NO_ARG_EXPANDER 20ve 20 ()sırasıyla alt arama için 2 bağımsız değişken olarak sayılır.

Bunları daha da genişletmek için aşağıdaki yardımcı makroları kullanırsak

##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses

Sondaki ,sonra CREATE_1bir iş-etrafında GCC / clang için, söyleyen bir (yanlış pozitif) hatası bastırmak olduğu ISO C99 requires rest arguments to be usedgeçerken -pedanticderleyici için. Bu FUNC_RECOMPOSER, MSVC için bir çözümdür veya makro çağrılarının parantezleri içindeki argüman sayısını (yani virgül) doğru şekilde sayamaz. Sonuçlar daha da çözüldü

FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);

Görmüş olabileceğiniz gibi, ihtiyacımız olan son tek adım, nihayet istenen makro sürüm adlarını seçmek için standart bir argüman sayma hilesi kullanmaktır:

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

sonuçları çözen

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

ve bize kesinlikle istenen, gerçek işlev çağrılarını verir:

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

Hepsini bir araya getirerek, daha iyi okunabilirlik için bazı ifadelerin yeniden düzenlenmesi ile 2 argüman örneğinin tüm kaynağı burada:

#include <stdio.h>

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main()
{
  create();
  create(10);
  create(20, 20);
  //create(30, 30, 30);  // Compilation error
  return 0;
}

API geliştiricisine karmaşık, çirkin ve yük getirmesine rağmen, C / C ++ işlevlerinin isteğe bağlı parametrelerini bize çılgın insanlara aşırı yüklemek ve ayarlamak için bir çözüm geliyor. Dışarıdan gelen aşırı yüklenmiş API'lerin kullanımı çok zevkli ve hoş hale geliyor. =)

Bu yaklaşımın başka olası bir basitleştirilmesi varsa, lütfen şu adresten bana bildirin:

https://github.com/jason-deng/C99FunctionOverload

Bu çalışmayı başarmama ilham veren ve beni yönlendiren tüm parlak insanlara yine özel teşekkürler! =)


3
Bunu 3 veya 4 işleve nasıl genişletebiliriz?
Phylliida

@Phylliida ideone.com/jD0Hm5 - sıfır ila beş bağımsız değişken desteklenir.
xx

9

Acı bir şekilde Visual C ++ ile çalışan bazı VA_NARGS çözümlerini arayan herkes için. Aşağıdaki makro benim için visual c ++ express 2010'da kusursuz bir şekilde (ayrıca sıfır parametrelerle!) Çalıştı:

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))) : 0

İsteğe bağlı parametrelere sahip bir makro istiyorsanız şunları yapabilirsiniz:

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

Bu, vc'de de benim için çalıştı. Ancak sıfır parametreler için çalışmaz.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8

Ben alıyorumunresolved external symbol _bool referenced in function _main
Avidan Borisov

evet bu bazı durumlarda olabilir. bunun farkında olmanız gerekir (#__ VA_ARGS__)? çalışma zamanında değerlendirildiği için diğer makrolardan farklıdır. Durumunuza bağlı olarak, kodun bu kısmını atlayabilirsiniz.
Syphorlate

2
Aslında mükemmel çalışan pastebin.com/H3T75dcn ile sonuçlandım (0 argüman da var).
Avidan Borisov

Bağlantı için teşekkürler ve evet bunu sizeof kullanarak da yapabilirsiniz ama benim için bu bazı durumlarda işe yaramadı ama prensip aynı (boolean değerlendirme).
Syphorlate

Başarısız olduğu bazı örnekler verebilir misiniz?
Avidan Borisov

7

gcc/ varargs makrolarınıg++ destekliyor ancak bunun standart olduğunu düşünmüyorum, bu yüzden riski size ait olacak şekilde kullanın.


4
C99'da standartlar ve C ++ 0x'e de ekleniyorlar.
greyfade

5
#include <stdio.h>

#define PP_NARG(...) \
    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
    PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
    63,62,61,60,                   \
    59,58,57,56,55,54,53,52,51,50, \
    49,48,47,46,45,44,43,42,41,40, \
    39,38,37,36,35,34,33,32,31,30, \
    29,28,27,26,25,24,23,22,21,20, \
    19,18,17,16,15,14,13,12,11,10, \
    9,8,7,6,5,4,3,2,1,0

#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

YASAL UYARI: Çoğunlukla zararsızdır.


kodunuzda bir hata var. lütfen yap :%s/MY_MACRO_/THINK_/g:)
João Portela

ayrıca, g ++ kullanan sıfır bağımsız değişkenlerle çalışmadıi686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela

1
Boş belirteç geçerli bir yer tutucu olduğundan, variadiac makroları için sıfır bağımsız değişken mevcut değildir.
Paul Fultz II

3

Önişlemci gerçekten bunun için tasarlanmadı.

Bununla birlikte, ciddi ölçüde zorlayıcı makro programlama alanına bir miktar okunabilirlik ile girmek istiyorsanız, Boost ön işlemci kitaplığına bir göz atmalısınız . Sonuçta, üç tamamen Turing uyumlu programlama seviyesi olmasaydı C ++ olmazdı (önişlemci, şablon metaprogramlama ve temel seviye C ++)!


3
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

Arama noktasında kaç argüman geçireceğinizi biliyorsunuz, bu yüzden gerçekten aşırı yüklemeye gerek yok.


2
Aslında özelliğin varlığını soruyordum.
Cenoc

3

Derek Ledbetter kodunun daha kısa versiyonu:

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

3

Korkunç makro canavarların büyük bir hayranı olarak, Jason Deng'in cevabını genişletmek ve onu gerçekten kullanılabilir hale getirmek istedim. (Daha iyisi veya daha kötüsü için.) Orijinali kullanmak pek hoş değil çünkü her yeni makro yapmak istediğinizde büyük alfabe çorbasını değiştirmeniz gerekiyor ve farklı miktarda argümana ihtiyacınız varsa daha da kötüsü.

Bu yüzden şu özelliklere sahip bir sürüm yaptım:

  • 0 argüman durumu çalışıyor
  • Dağınık kısımda herhangi bir değişiklik yapılmadan 1 ila 16 argüman
  • Daha fazla makro işlevi yazmak kolay
  • Gcc 10, clang 9, Visual Studio 2017'de test edilmiştir

Şu anda en fazla 16 argüman yaptım, ancak daha fazlasına ihtiyacınız varsa (gerçekten şimdi? Aptal olmaya başlıyorsunuz ...) FUNC_CHOOSER ve CHOOSE_FROM_ARG_COUNT ’u düzenleyebilir, ardından NO_ARG_EXPANDER’a birkaç virgül ekleyebilirsiniz.

Uygulama hakkında daha fazla ayrıntı için lütfen Jason Deng'in mükemmel cevabına bakın, ancak kodu buraya koyacağım:

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}

2

BOOST_PP_OVERLOADBir boostkitaplıktan kullanabilirsiniz .

Resmi destek belgesinden örnek :

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9

0

İhtiyacınız olan şeye bağlı olarak, bunu makro içeren değişken args ile yapabilirsiniz. Şimdi, isteğe bağlı parametreler veya makro aşırı yükleme, böyle bir şey yok.


-1

Makrolarla argümanları saymak için yukarıdaki örneklerden hiçbiri (Derek Ledbetter, David Sorkovsky ve Joe D'den) Microsoft VCC 10 kullanarak benim için işe yaradı. __VA_ARGS__Argüman her zaman tek bir argüman olarak kabul edilir (bununla birlikte ##veya değil), bu nedenle bu örneklerin dayandığı argüman değişimi işe yaramıyor.

Öyleyse, yukarıda birçokları tarafından belirtildiği gibi kısa cevap: hayır, makroları aşırı yükleyemez veya bunlar üzerinde isteğe bağlı argümanlar kullanamazsınız.


1
Yapabilirsiniz, ancak yalnızca C99 veya C ++ 11'de (__VA_ARGS__ olduğu için). VC2010, C89 / C ++ 03'tür (bazı C ++ 11 bitleri ortaya çıkmaya başlar, ancak bu henüz değil).
puetzk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.