使用 GCC 在 C 中重载函数 - 编译器警告

Function overloading in C using GCC - compiler warnings

我正在尝试在 C 中实现函数重载,而且我非常接近。我使用的是 C99,所以 C11 中引入的 _Generic 关键字对我不可用。我已经开发了一些工作代码,但是当我编译它时,我收到了一些警告。

工作示例:

#include <stdio.h>

#define print(x)                                                                        \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), print_int(x)   , \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string(x), \
(void)0))


void print_int(int i) {
    printf("int: %d\n", i);
}

void print_string(char* s) {
    printf("char*: %s\n", s);
}

int main(int argc, char* argv[]) {

    print(1);
    print("this");

    return 0;
}

编译会产生以下警告:

gcc overload.c -o main
overload.c: In function 'main':
overload.c:19: warning: passing argument 1 of 'print_string' makes pointer from integer without a cast
overload.c:20: warning: passing argument 1 of 'print_int' makes integer from pointer without a cast

有关更多调试信息,这是预处理器完成工作后主函数的样子:

int main(int argc, char* argv[]) {

 __builtin_choose_expr(__builtin_types_compatible_p(typeof(1), int ), print_int(1) , __builtin_choose_expr(__builtin_types_compatible_p(typeof(1), char[]), print_string(1), (void)0));
 __builtin_choose_expr(__builtin_types_compatible_p(typeof("this"), int ), print_int("this") , __builtin_choose_expr(__builtin_types_compatible_p(typeof("this"), char[]), print_string("this"), (void)0));

 return 0;
}

我怎样才能让编译警告消失并且仍然有工作代码?

可以通过在 #define 部分进行一些类型转换来抑制警告,但我觉得这可能不是最好的,甚至根本不是一个好的解决方案...

#define 部分中的函数调用更改为:

print_string((char*)x)
print_int((int)x)

我真的希望有人能提出更好的解决方案,因为这感觉不对...

理论上,这应该可行:

#define print(x)                                                                      \
(__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), print_int   , \
 __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string, \
(void)0))(x))

它选择 print_intprint_string,然后将所选函数应用到 x

内置的 GNU page 说未选择的分支可能仍会产生语法错误,但我看不出不匹配的类型是语法错误。

无论如何,当您将参数移至类型相关选择之外的函数时,您可以摆脱警告。所以(在伪代码中以使其更具可读性),而不是

choose_type_of(x, int, print_int(x), 
choose_type_of(x, char[], print_string(x), (void) 0))

choose_type_of(x, int, print_int, 
choose_type_of(x, char[], print_string, pass))(x)

这就是 user2357112 在评论中的建议。我正在研究类似的解决方案,但我很难让默认部分(上面的 pass)工作。当我使用 (void) 时,它应该扩展为 (void)(x),我收到有关括号不匹配的错误。

下面的解决方案创建了一个不使用其参数的默认打印函数。它可能是一个不存在的函数,因此在链接时出现问题或产生错误的其他原因。

#include <stdio.h>

#define print(x)                                                    \
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(x),   \
        int), print_int,                                            \
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(x),   \
        const char[]), print_string,                                \
    print_any))(x)


void print_int(int i) {
    printf("int: %d\n", i);
}

void print_string(const char *s) {
    printf("char[]: %s\n", s);
}

void print_any() {
    printf("unknown\n");
}

int main(void)
{
    int n = 9;
    const char str[] = "hello";

    print(n);
    print(str);

    print(1);
    print("this");

    print(3.2);

    return 0;
}

下面是一个示例,其中包含几种实现函数重载的方法。

一位发帖人提到了这个

The GNU page on the build-ins says that the branches not chosen may still generate syntax errors, but I fail to see how mismatched types are syntax errors.

我相信他们提到的语法错误是您收到的编译器警告,因为在预处理之后,编译器可以看到某些函数的参数类型,即使它们永远不会被调用也是错误的。解决方案(我认为这很巧妙)是找到一种方法来对编译器隐藏类型。显而易见的方法是可变参数,不太明显的方法是当前对 OP 问题的回答。

注意事项,即并非所有解决方案都是类型安全的,这完全特定于 GNU...

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

#define print(x)                                                                        \
  (__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), print_int   , \
   __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string, \
  (void)0))(x))

#define print1(x)                                                                          \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), print_int1(1,x)   , \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string1(1,x), \
 (void)0))

#define print2(x)                                                                    \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), printer(1,x), \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), printer(2,x), \
 (void)0))

#define TYPE_ID(x) __builtin_types_compatible_p(typeof(x), int   ) * 1 \
                +  __builtin_types_compatible_p(typeof(x), char[]) * 2  

#define print3(x) printer(TYPE_ID(x), x)

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

#define print4(x) \
    STATIC_ASSERT(TYPE_ID(x), __LINE__); \
    printer(TYPE_ID(x), x)

void printer(int i, ...) {
  va_list v;
  va_start(v, i); 
  switch(i) {
    case 1:{ 
             int arg = va_arg(v, int);
             printf("int: %d\n", arg);
             va_end(v);
             break;
           }
    case 2:{
             char * arg = va_arg(v, char*);
             printf("char*: %s\n", arg);
             va_end(v);
             break;
           }
    default: {
               fprintf(stderr, "Unknown type, abort\n");
               abort();
             }
  }
}

void print_int(int i) {
    printf("int: %d\n", i);
}

void print_string(char* s) {
    printf("char*: %s\n", s);
}
void print_int1(int i, ...) {
  va_list v;
  va_start(v, i);
  int arg = va_arg(v, int);
  printf("int: %d\n", arg);
  va_end(v);
}

void print_string1(int i, ...) {
  va_list v;
  va_start(v, i);
  char * arg = va_arg(v, char*);
  printf("char*: %s\n", arg);
  va_end(v);
}
int main(int argc, char* argv[]) {
  int  var    = 1729;
  double var1 = 1729;
  //Type safe
  //print(var1);//Comple time error
  print(var);
  print("print");

  /* Following are not Type Safe */
  print1(var1);// BAD... Does nothing.
  print1(var);
  print1("print1");

  print2(var1);// BAD... Does nothing.
  print2(var);
  print2("print2");

  //print3(var1);//Evil... Runtime error
  print3(var);
  print3("print3");

  //Type Safe
  //print4(var1);//Comple time error
  print4(var);
  print4("print4");
  return 0;
}

源在 github...

https://github.com/harryjackson/doc/blob/master/c/overload_c_functions.c

可以在这里找到具有多个参数的 switch 方法...

http://locklessinc.com/articles/overloading/

整个