Как добиться перегрузки функции в C? - PullRequest
224 голосов
/ 26 января 2009

Есть ли способ добиться перегрузки функции в C? Я смотрю на простые функции, которые будут перегружены, как

foo (int a)  
foo (char b)  
foo (float c , int d)

Я думаю, что нет прямого пути; Я ищу обходные пути, если таковые существуют.

Ответы [ 14 ]

4 голосов
/ 14 сентября 2017

Ответ Леушенко действительно классный - исключительно: пример foo не компилируется с GCC, который завершается ошибкой при foo(7), спотыкаясь о макросе FIRST и фактическом вызове функции ((_1, __VA_ARGS__), оставаясь с лишней запятой. Кроме того, у нас проблемы, если мы хотим обеспечить дополнительные перегрузки, такие как foo(double).

Поэтому я решил развить ответ немного дальше, в том числе допустить пустую перегрузку (foo(void) - это вызвало некоторые проблемы ...).

Идея теперь такова: определите более одного универсального в разных макросах и позвольте выбрать правильный в соответствии с количеством аргументов!

Количество аргументов довольно простое, исходя из этого ответа :

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

Это хорошо, мы разрешаем либо SELECT_1, либо SELECT_2 (или больше аргументов, если вы хотите / нуждаетесь в них), поэтому нам просто нужны соответствующие определения:

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1),    \
        int: foo_int,                   \
        char: foo_char,                 \
        double: foo_double              \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

ОК, я уже добавил void перегрузку & ndash; однако, этот на самом деле не охватывается стандартом C, который не допускает пустых переменных аргументов, т.е. е. тогда мы полагаются на расширения компилятора !

Вначале пустой вызов макроса (foo()) по-прежнему создает токен, но пустой. Таким образом, счетный макрос фактически возвращает 1 вместо 0 даже при пустом вызове макроса. Мы можем «легко» устранить эту проблему, если поставить запятую после __VA_ARGS__ условно , в зависимости от того, пустой список или нет:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

Это выглядело просто, но макрос COMMA довольно тяжелый; К счастью, эта тема уже освещена в блоге Jens Gustedt (спасибо, Jens). Основная хитрость в том, что макросы функций не раскрываются, если за ними не следуют круглые скобки, для дальнейших объяснений загляните в блог Йенса ... Нам просто нужно немного изменить макросы в соответствии с нашими потребностями (я собираюсь использовать более короткие имена и меньше аргументов для краткости).

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,

А теперь у нас все хорошо ...

Полный код в одном блоке:

/*
 * demo.c
 *
 *  Created on: 2017-09-14
 *      Author: sboehler
 */

#include <stdio.h>

void foo_void(void)
{
    puts("void");
}
void foo_int(int c)
{
    printf("int: %d\n", c);
}
void foo_char(char c)
{
    printf("char: %c\n", c);
}
void foo_double(double c)
{
    printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
    printf("double: %.2f, int: %d\n", c, d);
}

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
        int: foo_int,                \
        char: foo_char,              \
        double: foo_double           \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N

#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,

int main(int argc, char** argv)
{
    foo();
    foo(7);
    foo(10.12);
    foo(12.10, 7);
    foo((char)'s');

    return 0;
}
0 голосов
/ 09 марта 2016

Я надеюсь, что приведенный ниже код поможет вам понять перегрузку функции

#include <stdio.h>
#include<stdarg.h>

int fun(int a, ...);
int main(int argc, char *argv[]){
   fun(1,10);
   fun(2,"cquestionbank");
   return 0;
}
int fun(int a, ...){
  va_list vl;
  va_start(vl,a);

  if(a==1)
      printf("%d",va_arg(vl,int));
   else
      printf("\n%s",va_arg(vl,char *));
}
0 голосов
/ 26 января 2009

Попробуйте объявить эти функции как extern "C++", если ваш компилятор поддерживает это, http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspx

0 голосов
/ 26 января 2009

Разве вы не можете просто использовать C ++ и не использовать все другие функции C ++, кроме этой?

Если все еще нет строгого C, то я бы порекомендовал variadic functions .

...