3

После небольшого исследования и много отвода о я придумал с этим:VARIADIC Функция Перегрузки в C

#include <stdio.h> 

// Variadic Function Overloading: 

#define VOID "__variadic_VOID__" 

#define variadic_count(...) variadic_count_(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0) 
#define variadic_count_(_6, _5, _4, _3, _2, _1, count, ...) count 

#define variadic_token(func, ...) variadic_token_(func, variadic_count(__VA_ARGS__)) 
#define variadic_token_(func, count) variadic_token__(func, count) 
#define variadic_token__(func, count) funC## _ ## count 

#define variadic(func, ...)\ 
do {\ 
    if (#__VA_ARGS__ == "\"__variadic_VOID__\"")\ 
    {\ 
     variadic_token__(func, 0)();\ 
    }\ 
    else\ 
    {\ 
     variadic_token(func, __VA_ARGS__)(__VA_ARGS__);\ 
    }\ 
} while (0) 

// Usage: 

#define somefunction(...) variadic(somefunction, __VA_ARGS__) 

#define somefunction_0()\ 
do {\ 
    printf("somefunction_0(VOID)\n\n");\ 
} while (0) 

#define somefunction_1(x)\ 
do {\ 
    printf("somefunction_1(x = %i)\n\n", (x));\ 
} while (0) 

#define somefunction_2(x, y)\ 
do {\ 
    printf("somefunction_2(x = %i, y = %i)\n\n", (x), (y));\ 
} while (0) 

int main(int argc, char* argv[]) 
{ 
    //somefunction(); ERROR 
    somefunction(VOID); 
    somefunction(1); 
    somefunction(11); 
    somefunction(2, 3); 
    //somefunction(1, 2, 3); ERROR 

    printf("\n\n"); 
    return 0; 
} 

По существу, это позволяет проводить различие между нулем и единицей аргументами через специальный знак VOID. Он работает, если переменная function/macro не передается строковым литералом, определенным VOID, в качестве единственного аргумента. В этом случае только вызов somefunction("__variadic_VOID__"); вызовет неожиданное поведение; передавая переменную с тем же значением, что и строковый литерал, не вызывает неожиданного поведения.

Хотя предоставленный код работает только для аргументов 0-6, его можно модифицировать для работы с большим количеством аргументов.

Мне любопытно, однако, что if ("blah" == "blah") {doSomething();} оптимизирован для doSomething(); компилятором (Pelles C)? Или сравнение указателей происходит во время выполнения? Если он оптимизирован, то я думаю, что этот фрагмент кода позволяет легко и эффективно переадресации функций/макросов ... это правильно?

+3

Вы не можете использовать '==' для сравнения строк, он будет сравнивать указатели * с строками, а не с фактическими строками. –

+0

@JoachimPileborg Я протестировал код, указанный выше, и он работает; в этом случае сравнение выполняется между двумя строковыми литералами. – sweetname

+3

Равные строковые литералы могут совместно использовать хранилище, но не обязательно. Если две строки сравниваются с '==', они равны с 'strcmp', но это не так наоборот. Например, в 'char foo [] =" blah "; foo == "blah"; ', сравнение гарантированно будет ложным. – mafso

ответ

0

Гораздо лучше схема использовать это:

#define variadic(func, ...)\ 
do {\ 
    if (0 == *#__VA_ARGS__)\ 
    {\ 
     variadic_token__(func, 0)();\ 
    }\ 
    else\ 
    {\ 
     variadic_token(func, __VA_ARGS__)(__VA_ARGS__);\ 
    }\ 
} while (0) 

Тогда вызов как somefunction() будет расширяться на призыв somefunction_0() и нет никакой необходимости в магическом VOID лексемы. Это также намного безопаснее и, скорее всего, будет оптимизировано компилятором.

+0

Что делает звездочка? Кроме того, когда я заменил свое определение для моего и назвал 'somefunction();' получаю ошибку «недопустимое выражение» ... – sweetname

+0

@sweetname: он получает первый символ строкового литерала - это будет символ NUL, если и только если __VA_ARGS__ пуст –

+0

Проблема заключается в том, что если '__VA_ARGS__' пуст, то вызов' variadic_token (func, __VA_ARGS __) (__ VA_ARGS __); 'fail, потому что' variadic_token' имеет '...', который требует от минимум один токен. Несмотря на то, что этот код недоступен, он по-прежнему вызывает ошибку при компиляции. – sweetname