C ++ Makroları ile isteğe bağlı parametreleri almanın bir yolu var mı? Bir çeşit aşırı yükleme de hoş olurdu.
No you can't
C ++ Makroları ile isteğe bağlı parametreleri almanın bir yolu var mı? Bir çeşit aşırı yükleme de hoş olurdu.
No you can't
Yanıtlar:
İş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.
PRINT_STRING_MACRO_CHOOSER
gerekli mi? Direkt olarak iç gövdesiyle değiştirip tüm bunları ile çağırabilir miyim (__VA_ARGS__)
?
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!
XXX_X(,##__VA_ARGS__,` ...
atlayamıyoruz : XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); ``
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.
__FILE__
ve __LINE__
ve bu tür ...
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
, ##__VA_ARGS__
GCC / CLANG için özel ifade tarafından virgül yutma ve ##__VA_ARGS__
MSVC için örtük yutma ). Yani --std=c99
dilerseniz eksikleri derleyicinize iletmekten çekinmeyin =)Makul çapraz platformda çalışır , en azından test edilmiştir
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 create
ve ilgili gerçek fonksiyon tanımlama realCreate
ve 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_1
parça geliyor MACRO_CHOOSER(__VA_ARGS__)
ve (10)
parça ikinciden geliyor (__VA_ARGS__)
.
MACRO_CHOOSER
Eğ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 20
ve 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_1
bir iş-etrafında GCC / clang için, söyleyen bir (yanlış pozitif) hatası bastırmak olduğu ISO C99 requires rest arguments to be used
geçerken -pedantic
derleyici 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! =)
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
unresolved external symbol _bool referenced in function _main
gcc
/ varargs makrolarınıg++
destekliyor ancak bunun standart olduğunu düşünmüyorum, bu yüzden riski size ait olacak şekilde kullanın.
#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.
:%s/MY_MACRO_/THINK_/g
:)
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
Ö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 ++)!
#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.
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;
}
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:
Ş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;
}
BOOST_PP_OVERLOAD
Bir boost
kitaplı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
İ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.
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.