@Lundin: Хммм. Я получаю другое сообщение об ошибке с более новым GCC. Кажется, что не разрешено использовать аргументы с переменными значениями, но нельзя передавать аргументы с нулевым значением. Если это правда, добавление фиктивного аргумента работает. Я пытался использовать версию Visual Studio, по общему признанию, и она не компилируется вообще. (Это ничего не говорит о соответствии стандартам, хотя.)

я есть две функции foo1 (a, b) и foo2 (a, b, c) и макрос

#define add(a,b) foo(a,b)

Мне нужно переопределить макрос для достижения цели,

1. если add () вызывается с 2 параметрами, затем вызывается foo1

если add () вызывается с 3 параметрами, тогда вызывается foo2

Я новичок в опции VA_ARGS. Как мне это сделать

 Bathsheba06 дек. 2017 г., 13:48
Почему не могуadd быть перекодирован как функция, которая принимает переменный список аргументов?
 Lundin06 дек. 2017 г., 16:00
здравомыслящий решение: функцияtype add (type a, type b, type c); гдеc не является обязательным и может быть установлен в NULL или что-то подобное. Макросы Variadic часто являются дорогой к безумию.
 StoryTeller06 дек. 2017 г., 13:49
... Или, чтобы уменьшить вероятность ошибок, функция, которая принимает 2 обязательных параметра и еще один необязательный в виде списка переменных аргументов.

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

Этообычный трюк для подсчета аргументов может быть адаптировано для этого:

#define ADD_EXPAND(...) \
        ADD_EXPAND_(__VA_ARGS__, EXPAND_ADD_FUNCS())

#define ADD_EXPAND_(...) \
        EXPAND_ADD_SEL_FUNC(__VA_ARGS__)

#define EXPAND_ADD_SEL_FUNC(first_, second_, third_, func, ...) func

#define EXPAND_ADD_FUNCS() foo2, foo, dummy

#define add(...) ADD_EXPAND(__VA_ARGS__)(__VA_ARGS__)

После того, как вы пробьётесь через панель котла, в основном это просто размещение всех аргументов в строке с токенами функций после них и выяснение, какая функция выделяется. Это то чтоEXPAND_ADD_SEL_FUNC делает.

Вы можете видеть этожить на колиру.

Но я повторю то, что мы сказали вам в комментариях. Это, вероятно, будет решение, не соответствующее надлежащей функции. Я не проверил это полностью, поэтому поломка возможна. Используйте на свой риск.

Решение Вопроса

если тыдолжен используйте макросы variadic, тогда вот фокус.

#define add(...) _Generic ( &(int[]){__VA_ARGS__}, \
                            int(*)[2]: add2,       \
                            int(*)[3]: add3) (__VA_ARGS__)
Пусть макрос создаст составной массив литералов. Размер этого массива будет зависеть от количества аргументов.Захватите адрес составного литерала, чтобы получить тип указателя массива.Позволять_Generic проверьте, какой тип вы получили, а затем вызовите соответствующую функцию на основе этого.

Это 100% стандарт C, а также безопасный тип.

Демо-версия:

#include <stdio.h>


#define add(...) _Generic ( &(int[]){__VA_ARGS__}, \
                            int(*)[2]: add2,       \
                            int(*)[3]: add3) (__VA_ARGS__)

int add2 (int a, int b);
int add3 (int a, int b, int c);

int main (void)
{
  printf("%d\n", add(1, 2));
  printf("%d\n", add(1, 2, 3));
//printf("%d\n", add(1, 2, 3, 4)); Compiler error for this.
}


int add2 (int a, int b)
{
  return a + b;
}

int add3 (int a, int b, int c)
{
  return a + b + c;
}
 Lundin20 дек. 2017 г., 16:34
@JensGustedt Это преднамеренно, поскольку макросы типа variadic имеют несуществующую безопасность типов. Код будет компилироваться, только если переданы совместимые / неявно конвертируемые типы. Типы символов, с плавающей точкой и т. Д. Будут работать, но типы указателей на агрегаты и т. Д. Не будут приняты. Получение диагностики - это хорошо, неявные и бесшумные преобразования, и это плохо.
 Jens Gustedt20 дек. 2017 г., 13:43
Это решение имеет некоторые незначительные недостатки, а именно то, что аргументы функции должны быть совместимы с присваиваниемintи даже если они есть, вы можете получить странные предупреждения. Для последнего, используя_Complex long double вместоint позволит охватить все арифметические типы.

#define ADD(_1, _2, _3, X, ...) X
#define add(...) ADD(__VA_ARGS__, add3, add2, 0)(__VA_ARGS__)

Вспомогательный макросADD всегда выбирает четвертый аргумент:

add(a, b)    --> ADD(a, b, add3, add2, 0)    --> add2
add(a, b, c) --> ADD(a, b, c, add3, add2, 0) --> add3

Недостатком является то, что вы получаете довольно загадочные сообщения об ошибках, когда вы не предоставляете два или три аргумента функции.

Преимущество над функциями с переменным числом аргументов заключается в том, что вы получаете безопасность типов. Например, если ваши функции работаютdoubles, вы все еще можете сказатьadd(1, 2) и целочисленные аргументы будут преобразованы вdoubles. А для функций с переменным числом аргументов требуется некоторая дополнительная информация о количестве фактических аргументов, поэтому здесь это нереальное решение, если только вы не укажете количество слагаемых в функции.

добавлениеЯ изменилadd макрос, чтобы он не передавал пустой списокADD, Некоторые компиляторы допускают пустые списки, но это не стандартный C.

 Lundin06 дек. 2017 г., 16:28
Я не верю#define ADD(_1, _2, _3, X, ...) X стандарт C, вы должны сжечь__VA_ARGS__ где-то. Не компилируйте с некоторыми включенными расширениями gcc.
 StoryTeller06 дек. 2017 г., 14:15
Я должен сказать, что ваш способ более читабелен, чем мой выстрел. Определенно +1
 M Oehm06 дек. 2017 г., 20:56
@Lundin: Ох. Я не знал, что нужно использовать вариационный макрос__VA_ARGS__ где-то. Я получаю предупреждение в стандартном режиме. Странно, но я могу убрать это, добавив фиктивный аргумент послеadd2 вadd, что подозрительно, потому что это не меняет того факта, что я игнорирую аргументыADD, Это делает variadic args непустым списком.
 M Oehm07 дек. 2017 г., 09:19
@Lundin: Хммм. Я получаю другое сообщение об ошибке с более новым GCC. Кажется, что не разрешено использовать аргументы с переменными значениями, но нельзя передавать аргументы с нулевым значением. Если это правда, добавление фиктивного аргумента работает. Я пытался использовать версию Visual Studio, по общему признанию, и она не компилируется вообще. (Это ничего не говорит о соответствии стандартам, хотя.)
 Lundin06 дек. 2017 г., 16:40
Да, только что проверил с GCC в стандартном режиме. Этот код не скомпилируется. Очень умный трюк, но не действительный C, к сожалению.

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