13 ответов:
вот один из способов сделать это. Он использует список аргументов дважды, сначала для формирования имени вспомогательного макроса, а затем для передачи аргументов в этот вспомогательный макрос. Он использует стандартный трюк для подсчета количества аргументов в макросе.
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; }это делает его более легким для вызывающего макроса, но не для писателя.
С большим уважением к Дереку Ледбеттеру за его ответ - и с извинениями за возрождение старого вопроса.
получение понимания того, что он делал, и собирание в другом месте на способность предшествовать
__VA_ARGS__С##позволил мне придумать вариацию...// 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__)\ )для неспециалистов, таких как я, которые натыкаются на ответ, но не могут понять, как это работает, я пройду через фактическую обработку, начиная со следующего код...
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становится...
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) );который становится только шестым аргументом...
XXX_0(); XXX_1(1); XXX_2(1,2); XXX_3(1,2,3); XXX_4(1,2,3,4); 5;PS: удалите #define для XXX_0, чтобы получить ошибку компиляции [т. е.: если параметр без аргумента не разрешен].
PPS: было бы неплохо иметь недопустимые ситуации (например: 5) быть чем-то, что дает более четкую ошибку компиляции программисту!
PPPS: я не эксперт, поэтому я очень рад услышать комментарии (хорошие, плохие или другие)!
макросы C++ не изменились с C. Поскольку у C не было перегрузки и аргументов по умолчанию для функций, у него, конечно же, не было их для макросов. Поэтому, чтобы ответить на ваш вопрос: нет, эти функции не существуют для макросов. Ваш единственный вариант-определить несколько макросов с разными именами (или не использовать макросы вообще).
в качестве примечания: в C++ обычно считается хорошей практикой как можно больше отходить от макросов. Если вам нужны такие функции, есть хороший возможно, вы злоупотребляете макросами.
С уважением ко Дерек Ледбеттер,Давид Сорковский,Syphorlate для их ответов, вместе с гениальным методом обнаружения пустых аргументов макроса по Йенс Gustedt at
https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
наконец я выхожу с чем-то, что включает в себя все трюки, так что решение
- использует только стандартный C99 макросы для достижения перегрузки функций, без расширения GCC / CLANG / MSVC (т. е. проглатывание запятой конкретным выражением
, ##__VA_ARGS__для GCC / CLANG и неявного проглатывания##__VA_ARGS__для MSVC). Так что не стесняйтесь передавать недостающие--std=c99для вашего компилятора, если вы хотите=)- работает ноль аргументов, а также неограниченное количество аргументов, если вы расширить его далее, чтобы удовлетворить ваши потребности
работает достаточно кросс-платформенный, по крайней мере, проверены на
- GNU / Linux + GCC (GCC 4.9.2 на CentOS 7.0 x86_64)
- GNU / Linux + CLANG / LLVM, (CLANG/LLVM 3.5.0 на CentOS 7.0 x86_64)
- OS X + Xcode, (XCode 6.1.1 на OS X Yosemite 10.10.1)
- Windows + Visual Studio, (Visual Studio 2013 Обновление 4 на Windows 7 SP1 64 бит)
для лентяев, просто перейдите к самому последнему из этого сообщения, чтобы скопировать источник. Ниже приводится подробное объяснение, которое, надеюсь, помогает и вдохновляет всех людей, ищущих общего
__VA_ARGS__решений, как мне. =)вот как это происходит. Сначала определите видимую пользователем перегруженную "функцию", я назвал ее
create, и соответствующее фактическое определение функцииrealCreate, и определения макросов с разное количество аргументовCREATE_2,CREATE_1,CREATE_0, как показано ниже:#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)The
MACRO_CHOOSER(__VA_ARGS__)часть в конечном итоге разрешает имена определений макросов, а вторая(__VA_ARGS__)часть содержит их списки параметров. Таким образом, вызов пользователяcreate(10)разрешаетCREATE_1(10)наCREATE_1часть отMACRO_CHOOSER(__VA_ARGS__)и(10)часть происходит от второго(__VA_ARGS__).The
MACRO_CHOOSERиспользует трюк, что если__VA_ARGS__пусто, следующее выражение объединенный в допустимый вызов макроса препроцессором:NO_ARG_EXPANDER __VA_ARGS__ () // simply shrinks to NO_ARG_EXPANDER()гениально, мы можем определить этот результирующий вызов макроса как
#define NO_ARG_EXPANDER() ,,CREATE_0обратите внимание на две запятые, они объясняются в ближайшее время. Следующий полезный макрос
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())так что призывы
create(); create(10); create(20, 20);фактически расширена до
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);как следует из названия макроса, мы должны подсчитать количество аргументов позже. Вот еще один трюк: препроцессор не только простая замена текста. Он выводит количество аргументов вызова макроса только из числа запятых, которые он видит внутри скобок. Фактические "аргументы", разделенные запятыми, не должны иметь допустимого синтаксиса. Они могут быть любым текстом. То есть, в приведенном выше примере
NO_ARG_EXPANDER 10 ()считается 1 аргументом для среднего вызова.NO_ARG_EXPANDER 20и20 ()учитываются как 2 аргумента для нижнего вызова соответственно.если мы используем следующие вспомогательные макросы для дальнейшего расширения их
##define CHOOSE_FROM_ARG_COUNT(...) \ FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, )) #define FUNC_RECOMPOSER(argsWithParentheses) \ FUNC_CHOOSER argsWithParenthesesтрейлинг
,послеCREATE_1является обходной для GCC / CLANG, подавляя (ложноположительную) ошибку, говоря, чтоISO C99 requires rest arguments to be usedпри прохождении-pedanticдля вашего компилятора. ЭлементFUNC_RECOMPOSERэто обходной путь для MSVC, или он не может правильно подсчитать количество аргументов (т. е. запятых) внутри скобок вызовов макросов. Результаты далее разрешаются в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);как орлиный глаз вы, возможно, видели, последний единственный шаг, который нам нужно, это использовать стандартный трюк подсчета аргументов, чтобы, наконец, выбрать нужные имена версий макросов:
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3который разрешает результаты
CREATE_0(); CREATE_1(10); CREATE_2(20, 20);и, конечно, дает нам желаемые, фактические вызовы функций:
realCreate(0, 0); realCreate(10, 10); realCreate(20, 20);собирая все вместе, с некоторой перестановкой операторов для лучшей читаемости,весь источник 2-аргументного примера здесь:
#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 разработчик, приходит решение для перегрузки и установки дополнительных параметров функций C/C++ к нам сумасшедшие люди. Использование исходящих перегруженных API становится очень приятным и приятным. =)
если есть еще какое-либо возможное упрощение этого подхода, пожалуйста, дайте мне знать на
https://github.com/jason-deng/C99FunctionOverload
еще раз особая благодарность всем блестящим людям, которые вдохновляли и вели меня для достижения этой работой! = )
для тех, кто мучительно ищет какое-то решение VA_NARGS, которое работает с Visual C++. Следующий макрос работал для меня безупречно (также с нулевыми параметрами!) в visual C++ express 2010:
#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Если вы хотите макрос с дополнительными параметрами вы можете сделать:
//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__)))это сработало и для меня в vc. Но это не работает для нулевых параметров.
int x=99; x=mymacro(2);//2 x=mymacro(2,2);//4 x=mymacro(2,2,2);//8
gcc/g++поддерживает макросы varargs но я не думаю, что это стандарт, так что используйте его на свой страх и риск.
#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 */ }отказ от ответственности:в основном безобидна.
Это не совсем то, для чего предназначен препроцессор.
тем не менее, если вы хотите войти в область серьезно сложного макропрограммирования с небольшой читабельностью, вы должны взглянуть на повысить библиотеку препроцессоров. В конце концов, это не было бы C++, если бы не было трех полностью совместимых уровней программирования Turing (препроцессор, метапрограммирование шаблонов и базовый уровень 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)вы знаете в точке вызова, сколько args вы собираетесь передать, так что нет никакой необходимости в перегрузке.
в зависимости от того, что вам нужно, вы можете сделать это с var args с макросами. Теперь, необязательные параметры или макрос перегрузки, нет такой вещи.
более краткая версия кода Дерека Ледбеттера:
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; }
можно использовать
BOOST_PP_OVERLOADСboostбиблиотека.пример официальный boost doc:
#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
ни один из приведенных выше примеров (от Дерека Ледбеттера, Дэвида Сорковского и Джо Д) для подсчета аргументов с макросами не работал для меня с помощью Microsoft VCC 10. Элемент
__VA_ARGS__аргумент всегда рассматривается как один аргумент (маркер-изинг его с##или нет), поэтому смещение аргумента, в котором полагаются эти примеры, не работает.Итак, короткий ответ, как указано многими другими выше: нет, вы не можете перегружать макросы или использовать на них необязательные аргументы.
Comments