在C中不使用泛型的函数重载

在C中不使用泛型的函数重载,c,function,generic-programming,overloading,C,Function,Generic Programming,Overloading,我希望在C中完成函数重载,但我试图在不支持C11的Unix服务器上运行代码,因此\u Generic关键字不可用 (升级服务器以使其具有更新版本的GCC不是一个选项) 除了使用\u Generic模拟C中的有效函数重载,还有其他方法吗?对于某些参数类型,您可以执行有限形式的重载,如: void func_int(int); void func_long(long); void func_longlong(long long); #define FUNC(X) \ (sizeof(X) &

我希望在C中完成函数重载,但我试图在不支持C11的Unix服务器上运行代码,因此
\u Generic
关键字不可用

(升级服务器以使其具有更新版本的GCC不是一个选项)


除了使用
\u Generic
模拟C中的有效函数重载,还有其他方法吗?

对于某些参数类型,您可以执行有限形式的重载,如:

void func_int(int);
void func_long(long);
void func_longlong(long long);

#define FUNC(X) \ 
  (sizeof(X) <= sizeof(int) ? func_int(X) \
  : sizeof(X) == sizeof(long) ? func_long(X) \
  : func_longlong(X))
void func_int(int);
无效函数长(长);
void func_longlong(长-长);
#定义FUNC(X)\

(sizeof(X)我发现了一个似乎可以工作的方法,但是在编译时我仍然收到一些警告

工作代码:

#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
显式显示GNU99(
-std=GNU99
)解决方案,至少从版本开始

当然,还有一些限制:所有变量必须具有相同的返回类型,所有函数变量必须具有语法意义。后者通常是各种编译错误的原因(函数变量参数的类型无效)。这可以通过声明没有参数原型的函数来避免;但是,必须记住,默认类型升级将随之发生(
float
升级为
double
,所有小于
int
的整数类型升级为
int
unsigned int
)考虑这个示例程序:

#define  _GNU_SOURCE /* for asprintf() */
#include <stdlib.h>
#include <stdio.h>

typedef struct {
    double  x;
    double  y;
    double  z;
    double  d;
} plane;

static const char *foo_char_array();
static const char *foo_int();
static const char *foo_long();
static const char *foo_double();
static const char *foo_float();
static const char *foo_short();
static const char *foo_plane();

#define foo(x) \
    ( __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), int),     foo_int(x),        \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), long),    foo_long(x),       \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), short),   foo_short(x),      \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), float),   foo_float(x),      \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), double),  foo_double(x),     \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), plane),   foo_plane(x),      \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), char []), foo_char_array(x), \
      (void)0 ))))))) )


int main(void)
{
    double d = 1.0;
    float  f = 2.0f;
    short  s = 3;
    long   n = 4L;
    plane  p = { 5.0, 6.0, 7.0, 8.0 };

    printf("foo(9) = %s\n", foo(9));
    printf("foo(10L) = %s\n", foo(10L));
    printf("foo(11.0f) = %s\n", foo(11.0f));
    printf("foo(12.0) = %s\n", foo(12.0));
    printf("foo(\"bar\") = %s\n", foo("bar"));
    printf("foo(d) = %s\n", foo(d));
    printf("foo(f) = %s\n", foo(f));
    printf("foo(s) = %s\n", foo(s));
    printf("foo(n) = %s\n", foo(n));
    printf("foo(p) = %s\n", foo(p));
    return EXIT_SUCCESS;
}

static const char *foo_char_array(char x[]) { return "char []"; }
static const char *foo_int(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(int)%d", x); return (const char *)buffer; }
static const char *foo_long(long x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(long)%ld", x); return (const char *)buffer; }
static const char *foo_float(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%af", x); return (const char *)buffer; }
static const char *foo_double(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%a", x); return (const char *)buffer; }
static const char *foo_short(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(short)%d", x); return (const char *)buffer; }
static const char *foo_plane(plane p) { static char buffer[120]; snprintf(buffer, sizeof buffer, "(plane){ .x=%g, .y=%g, .z=%g, .d=%g }", p.x, p.y, p.z, p.d); return (const char *)buffer; }

在32位x86 Linux(ILP32)和x86-64(LP64)上测试。是的,上面的程序将泄漏内存,因为它从不
free()
s由
foo...返回的动态分配字符串
函数变体。

在联合中使用函数指针和无名结构是可能的。下面是一个例子,我们重载add和mul函数。有两个联合LIBI和LIBF包含无名结构。LIBI包含函数指针add和mulc,它们只使用整数值。LIBF是ame为LIBI,除了add和mul使用浮点变量外。此外,我们需要在这些联合之外创建addi、muli、addf和mulf函数。联合中的函数指针将引用这4个函数。例如,addi使用int值,而addi使用浮点值,所以addi LIBI使用addf变量。这个例子也可以用来模拟C语言中没有的命名空间。联合的作用类似于这个例子中的命名空间

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

union {
  struct {
    void (*add)(int *, int);
    void (*mul)(int *, int);
   };
   }LIBI;

union {
   struct {
   void (*add)(float *, float);
   void (*mul)(float *, float);
   };
 }LIBF;

void addi(int *a, int c){
  *a += c; 
 }

void addf(float *a, float c){
  *a += c;  
  }
void muli(int *a, int c){
  *a *= c; 
  }

void mulf(float *a, float c){
   *a *= c; 
  }

 int main(void){

 LIBI.add = addi;
 LIBF.add = addf;
 LIBI.mul = muli;
 LIBF.mul = mulf;

 int ia = 10;
 int ib = 2;
 float fa = 20.0f;
 float fb = 2.0f;

 LIBI.add(&ia,ib);
 LIBF.add(&fa,fb);

 printf("%d\n",ia);
 printf("%f\n",fa);

 LIBI.mul(&ia,ib);
 LIBF.mul(&fa,fb);

 printf("%d\n",ia);
 printf("%f\n",fa);

 return 0; 
 }
#包括
#包括
联合{
结构{
无效(*添加)(整数*,整数);
无效(*mul)(整数*,整数);
};
}李比;
联合{
结构{
无效(*添加)(浮动*,浮动);
无效(*mul)(浮动*,浮动);
};
}LIBF;
无效addi(整数*a,整数c){
*a+=c;
}
无效addf(浮动*a,浮动c){
*a+=c;
}
无效muli(整数*a,整数c){
*a*=c;
}
无效mulf(浮动*a,浮动c){
*a*=c;
}
内部主(空){
LIBI.add=addi;
LIBF.add=addf;
LIBI.mul=muli;
LIBF.mul=mulf;
intia=10;
int-ib=2;
浮球fa=20.0f;
浮球fb=2.0f;
LIBI.add(&ia,ib);
LIBF.add(&fa,fb);
printf(“%d\n”,ia);
printf(“%f\n”,fa);
LIBI.mul(&ia,ib);
LIBF.mul(和fa,fb);
printf(“%d\n”,ia);
printf(“%f\n”,fa);
返回0;
}

不是真的;您必须确定在每个位置应该调用哪个函数,并调用正确的函数,而不是让
\u Generic
或多或少自动为您这样做。为什么您不能安装GCC 5.3.0(比如)在
$HOME/gcc/bin
中,然后使用它来构建软件?gcc只是一个程序;如果你可以编译并安装真正需要
\u Generic
的二进制文件,你可以编译并安装一个二进制文件,为
\u Generic
提供支持,即使机器上没有其他人知道它在那里se附带了RedHat版本。Unix管理员是唯一可以接触它的人,唯一的方法是下一次RedHat升级,这不是很快的事。胡说八道。你不必使用
/bin/gcc
/usr/bin/gcc
;你可以使用“他们”版本的gcc构建“你的”gcc版本,然后使用“你的”“GCC来构建您的软件。我一直在RHEL系统上使用古老的编译器,这些编译器无法诊断现代编译器可以诊断的所有问题。也许来自Jens Gustedt的P99的代码可能有用。不过我自己还没有使用过它,我不知道它会增加多少预处理开销。@JonathanLeffler,+1,这是肯定的基本上是可能的,但在某些环境中,权力机构不赞成使用“未经批准”的编译生产代码,安装自己的编译器并使用它将是一个坏主意。我不知道这里的情况是否如此,或者OP的代码是否仅供个人使用,但这并不总是一个好主意,即使它在技术上是可能的。你必须将其视为_Generic.Move(x)在外面,警告就会消失。你说把(x)移到外面是什么意思?
定义打印(x)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\(void)0))(x)
code-only答案不受鼓励,因为它们不会为未来的读者提供太多信息。请对您所写的内容提供一些解释
foo(9) = (int)9
foo(10L) = (long)10
foo(11.0f) = 0x1.6p+3f
foo(12.0) = 0x1.8p+3
foo("bar") = char []
foo(d) = 0x1p+0
foo(f) = 0x1p+1f
foo(s) = (short)3
foo(n) = (long)4
foo(p) = (plane){ .x=5, .y=6, .z=7, .d=8 }
#include<stdio.h>
#include<stdlib.h>

union {
  struct {
    void (*add)(int *, int);
    void (*mul)(int *, int);
   };
   }LIBI;

union {
   struct {
   void (*add)(float *, float);
   void (*mul)(float *, float);
   };
 }LIBF;

void addi(int *a, int c){
  *a += c; 
 }

void addf(float *a, float c){
  *a += c;  
  }
void muli(int *a, int c){
  *a *= c; 
  }

void mulf(float *a, float c){
   *a *= c; 
  }

 int main(void){

 LIBI.add = addi;
 LIBF.add = addf;
 LIBI.mul = muli;
 LIBF.mul = mulf;

 int ia = 10;
 int ib = 2;
 float fa = 20.0f;
 float fb = 2.0f;

 LIBI.add(&ia,ib);
 LIBF.add(&fa,fb);

 printf("%d\n",ia);
 printf("%f\n",fa);

 LIBI.mul(&ia,ib);
 LIBF.mul(&fa,fb);

 printf("%d\n",ia);
 printf("%f\n",fa);

 return 0; 
 }