Могут ли макросы быть перегружены количеством аргументов?

Какэтот Работа? Как реализовать вариационный макрос C99 / C ++ 11 для расширения до разных вещей только на основании того, сколько аргументов ему дано?

 dyp22 мая 2013 г., 08:19
Безразлично»не могу ответить, как это работает, но если кому-то просто нужна реализация:буст препроцессор

Ответы на вопрос(5)

Ниже приведено улучшениеPotatoswatter»ответ, которыйМожно различать ноль и один аргумент.

В двух словах, когда__VA_ARGS__ пустой,EXPAND __VA_ARGS__ () внутриVA_SIZE макрос становитсяEXPAND () и заменяется 6 запятыми. Так,VA_SIZE... становитсяCOMPOSE( GET_COUNT, (,,,,,, , 0, 6, 5, 4, 3, 2, 1) )и это становитсяGET_COUNT (,,,,,, , 0, 6, 5, 4, 3, 2, 1) и возвращает 0.

С другой стороны, когда__VA_ARGS__ например,int, 5EXPAND __VA_ARGS__ () становитсяEXPAND int, 5 (), Так,VA_SIZE... становитсяCOMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) ), который становитсяGET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) и возвращает 2, как описано в Potatoswatter 'ответ.

Я получилEXPAND идея отДжейсон Дангответ.

Код библиотеки:
#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )
#define COMPOSE( NAME, ARGS ) NAME ARGS

#define GET_COUNT( _0, _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define EXPAND() ,,,,,, // 6 commas (or 7 empty tokens)
#define VA_SIZE( ... ) COMPOSE( GET_COUNT, (EXPAND __VA_ARGS__ (), 0, 6, 5, 4, 3, 2, 1) )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)
Использование:
#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )

#define MY_OVERLOADED_0( ) meh()
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()

MY_OVERLOADED()                // meh()
MY_OVERLOADED(bool)            // foo< bool >
MY_OVERLOADED(int, 5)          // bar< int >( 5 )
MY_OVERLOADED(me, double, now) // bang_me< double >.now()
 CaptJak18 июл. 2017 г., 21:46
Не уверен, что я чувствую по поводу этого последнего комментария ...
 Innocent Bystander18 июл. 2017 г., 22:04
@CaptJak не моя вина. ОП придумал этот пример. : D
 Th. Thielemann18 дек. 2018 г., 13:40
Это дает мне:iso c99 requires rest arguments to be used, И я должен использовать GCC-pedantic переключиться по причинам проекта.

Я расширил решение от Potatowater 'чтобы избежатьiso c99 requires rest arguments to be used проблема, когда GCC 'переключатель компилятора-pedantic используется

Библиотека

#define NUM_ARGS_(_1, _2, _3, _4, _5, _6, _7, _8, TOTAL, ...) TOTAL
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define CONCATE_(X, Y) X##Y
#define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER)
#define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS (__VA_ARGS__))(__VA_ARGS__)

настройка

#define MY_OVERLOADED(...) VA_MACRO(MY_OVERLOADED, void, void, __VA_ARGS__)
#define MY_OVERLOADED0(s, t) MacroTest()
#define MY_OVERLOADED1(s, t, a) MacroTest( a)
#define MY_OVERLOADED2(s, t, a, b) MacroTest(a, b)
#define MY_OVERLOADED3(s, t, a, b, c) MacroTest(a, b, c)

использование

MY_OVERLOADED();
MY_OVERLOADED(1);
MY_OVERLOADED(11, 22);
MY_OVERLOADED(111, 222, 333);
Решение Вопроса

(Изменить: см. Конец для готового решения.)

Чтобы получить перегруженный макрос, сначала нам нужен макрос, который выбирает между несколькими реализациями. Эта часть нене использовать вариадный макрос. Затем вариационный макрос, который в общем случае считает свои аргументы, создает селектор. Включение счетчика аргументов в диспетчер приводит к перегруженному макросу.

Предостережение: Эта система не может определить разницу между нулем и одним аргументом, потому чтоявляется нет разницы между аргументом и одним пустым аргументом. Они оба похожи.MACRO()

Для выбора между реализациями используйте оператор связывания макросов с рядом функциональных макросов.

#define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ )
#define impl_1() meh
#define impl_2( abc, xyz ) # abc "wizza" xyz()
//etc

// usage: select( 1 ) => impl_1() => meh
//        select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar()

Поскольку## оператор подавляет макроразложение своих аргументов, это 'лучше обернуть его в другой макрос.

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

Для подсчета аргументов используйте__VA_ARGS__ чтобы сместить аргументы следующим образом (это умная часть):

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )
Код библиотеки:
#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)
Использование:
#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()
 chris22 мая 2013 г., 06:06
@ Оли, скажи, что передал один аргументVA_SIZE, Это расширилось бы доGET_COUNT(x, 6, 5, 4, 3, 2, 1), который отменяетx с_1,6 с_2и так далее, и выосталось с1 являющийсяCOUNT, С двумя аргументами, еще один получает и2 становится.COUNT
 chris22 мая 2013 г., 06:10
@ Оли, да, но этотам слишком плохоs ограничение количества аргументов, с которыми он будет работать, следовательно, "до тошноты ", Вы всегда можете добавить столько, сколько вамЯ хотел бы поддержать.
 Matthieu M.22 мая 2013 г., 08:57
Знаете ли вы, есть ли поддержка (например, в Boost) для надежного определения количества элементов__VA_ARGS__ ? Я думаю, что некоторые компиляторы предлагают встроенные функции для него, и Boost обычно можно рассчитывать на использование соответствующего встроенного в зависимости от компилятора.
 Oli22 мая 2013 г., 06:02
Спасибо, это отличное объяснение. Может быть, немного больше деталей о "умная" часть была бы хороша.
 Asaf03 июл. 2016 г., 17:46
Обратите внимание, что в MSVC, похоже, есть ошибка. Вот'Обходной путь:stackoverflow.com/questions/5134523/...
 Oli22 мая 2013 г., 06:08
@ Крис, действительно, это умно
 chris22 мая 2013 г., 05:50
Аккуратно, я неЯ не знаю, что ты мог бы надежно сделать это.
 Potatoswatter22 мая 2013 г., 10:04
@MatthieuM. Я'м в основном не знают о существующих утилитах. Отсюда открывается этот вопрос: vP. Я'Я поддерживаю препроцессор C ++, но я в основном избегаю метапрограммирования препроцессора, поэтому практические библиотеки в основном меня не интересуют. И документы Boost.PP необлегчить понимание того, чтоневозможно или почему. Так… может быть, открыть еще один Q.

Я бы опубликовал это как комментарий к Potatoswatter 'пост, но этоСлишком длинный и требует листинга кода.

Вот немного кода на Perl для генерации набора макросов, которые предназначены для перегруженных макросов.

$ perl -le 'map{
        $arity = $_; map {
                $ar = 2 + $arity + $_; $arm = $ar - 1; $arlist = join("", map{"A$_, "} 1..$arity); $warlist = "WHAT, $arlist";
                @li = map {"_$_"} 0..$_; $lis = join(", ", @li); $lim = pop @li; $lims = join(", ", @li);
                print "#define FEI_${arity}A_$ar($warlist$lis) FEI_${arity}A_$arm($warlist$lims) WHAT($_, $arlist$lim)"
        } 1..3; print ""
} 0..4'

Вот вывод скрипта:

#define FEI_0A_3(WHAT, _0, _1) FEI_0A_2(WHAT, _0) WHAT(1, _1)
#define FEI_0A_4(WHAT, _0, _1, _2) FEI_0A_3(WHAT, _0, _1) WHAT(2, _2)
#define FEI_0A_5(WHAT, _0, _1, _2, _3) FEI_0A_4(WHAT, _0, _1, _2) WHAT(3, _3)

#define FEI_1A_4(WHAT, A1, _0, _1) FEI_1A_3(WHAT, A1, _0) WHAT(1, A1, _1)
#define FEI_1A_5(WHAT, A1, _0, _1, _2) FEI_1A_4(WHAT, A1, _0, _1) WHAT(2, A1, _2)
#define FEI_1A_6(WHAT, A1, _0, _1, _2, _3) FEI_1A_5(WHAT, A1, _0, _1, _2) WHAT(3, A1, _3)

#define FEI_2A_5(WHAT, A1, A2, _0, _1) FEI_2A_4(WHAT, A1, A2, _0) WHAT(1, A1, A2, _1)
#define FEI_2A_6(WHAT, A1, A2, _0, _1, _2) FEI_2A_5(WHAT, A1, A2, _0, _1) WHAT(2, A1, A2, _2)
#define FEI_2A_7(WHAT, A1, A2, _0, _1, _2, _3) FEI_2A_6(WHAT, A1, A2, _0, _1, _2) WHAT(3, A1, A2, _3)

#define FEI_3A_6(WHAT, A1, A2, A3, _0, _1) FEI_3A_5(WHAT, A1, A2, A3, _0) WHAT(1, A1, A2, A3, _1)
#define FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) FEI_3A_6(WHAT, A1, A2, A3, _0, _1) WHAT(2, A1, A2, A3, _2)
#define FEI_3A_8(WHAT, A1, A2, A3, _0, _1, _2, _3) FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) WHAT(3, A1, A2, A3, _3)

#define FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) FEI_4A_6(WHAT, A1, A2, A3, A4, _0) WHAT(1, A1, A2, A3, A4, _1)
#define FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) WHAT(2, A1, A2, A3, A4, _2)
#define FEI_4A_9(WHAT, A1, A2, A3, A4, _0, _1, _2, _3) FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) WHAT(3, A1, A2, A3, A4, _3)

Это (регулярно структурированные разделы) групп макро-перегрузок, которые используются для генерацииFOR_EACH (А.к.а.FE) макросы, которые могут отправлятьWHAT макрос необязательно с произвольным числом постоянных аргументов (,A1A2...) в дополнение к произвольному количеству аргументов в списке вместе с индексом в правильном порядке (наивная реализация без использования чего-то вродеSELECT за перегрузку дали бы обратные индексы).

Как пример, оставшийся раздел (нерегулярныйбазовый вариант" часть второго блока) выглядит следующим образом:

#define FE_INDEXED_1ARG(...) VA_SELECT(FEI_1A, __VA_ARGS__)
#define FEI_1A_3(WHAT, A1, _0) WHAT(0, A1, _0)

Полезность этого может быть подвергнута сомнению (я построил это, потому что я видел использование для этого ...), и это также не отвечает на OP 'с вопросом (на самом деле, это своего рода противоположность - конструкция foreach делаеттак же вещь для всех вариативных аргументов ...), но я просто подумал, что техника довольно интересная (а также ужасающая в некоторых отношениях) и допускает довольно выразительные возможности при использовании препроцессора, и будет возможно создать очень эффективную машину код таким образом. Я думаю, что это также служит ярким примером того, почему я лично думаю, что препроцессор C все еще имеет возможности для улучшения.

Под этим я подразумеваю, что препроцессор C - абсолютная мерзость, и мы, вероятно, должны отказаться от него и начать с нуля :)

Хотя этоЯ уже ответил, я подготовил очень короткую версию. Надеюсь, это может помочь.

Реализация
// Variable Argument Macro (VA_MACRO) upto 6 arguments
#define NUM_ARGS_(_1, _2, _3, _4, _5, _6, TOTAL, ...) TOTAL
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1)

#define CONCATE_(X, Y) X##Y  // Fixed the double '_' from previous code
#define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER)
#define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)
настройка
// This is how user may define own set of variadic macros
#define MY_MACRO(...) VA_MACRO(MY_MACRO, __VA_ARGS__)
#define MY_MACRO1(_1) "One"
#define MY_MACRO2(_1, _2) "Two"
#define MY_MACRO3(_1, _2, _3) "Three"
использование
// While using those, user needs to use only the main macro
int main ()
{
  auto one = MY_MACRO(1);
  auto two = MY_MACRO(1, 2); 
  auto three = MY_MACRO(1, 2, 3); 
}
 Otzen21 июл. 2017 г., 13:51
Нашел этот ответ полезным, так как код легче читать. Прочитав это, упростил понимание @Potatoswatter 'ответ. так что голос за этот ответ тоже.
 Innocent Bystander11 июл. 2017 г., 16:42
Я лично нахожу ответ @Potatoswatter более читабельным
 Potatoswatter26 дек. 2016 г., 08:13
Это'не обобщены. Это'Практически тот же код, но никакого объяснения не приложено. Возможно, я должен поставить код первым, а описание вторым.__ ошибка; он зарезервирован для разработчика компилятора и не разрешен для пользователя.
 Potatoswatter26 дек. 2016 г., 04:50
Чем это отличается от моего ответа? Единственное, что я вижу, это двойное подчеркивание, что является настоящей ошибкой, которую некоторые компиляторы будут отмечать.
 iammilind26 дек. 2016 г., 06:20
@Patatoswatter, это 'не сильно отличается, за исключением того, что этолегко понять, обобщить, упростить и назвать макросы его аргумент более ясен для новичка. Когда я прочитал ваш ответ, я полностью растерялся и смог понятьфактический» Решить сам с некоторой помощью из вашего ответа. относительно__Я намеренно сохранил (как вы можете видеть в комментарии), потому что, как правило, люди будут иметьCONCATE & CONCATE_ их комбинация для объединения двух строк, следовательно, эта часть в любом случае исчезнет. Подумай об этом__» в качестве заполнителя для понимания.

Ваш ответ на вопрос