C语言中的函数重载而不使用_Generic

时间:2016-04-08 16:00:57

标签: c function generic-programming overloading

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

(升级服务器使其具有较新版本的GCC不是一种选择)。

有没有其他方法可以使用_Generic来模拟C中的有效函数重载?

4 个答案:

答案 0 :(得分:3)

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

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))

这将允许您使用FUNC(i)并让它调用不同的功能。它是有限的,因为你只能按大小来区分类型。这意味着,如果sizeof(int) == sizeof(long),则您永远不会致电func_long,如果sizeof(long) == sizeof(long long),则永远不会致电func_longlong。此外,如果double与您正在测试的整数类型之一相同,则不能为其他类型(例如sizeof(double))重载。

它可以用于例如过载floatdoublelong double,您可能有不同的函数实现,根据参数类型的精度(即位数)计算得更多或更少。

答案 1 :(得分:3)

GCC manual明确显示GNU99(-std=gnu99)解决方法,因为至少版本为3.1.1

当然有一些限制:所有变体必须具有相同的返回类型,并且所有函数变体必须具有语法意义。后者通常是各种编译错误的原因(函数变量参数的类型无效)。通过声明没有参数原型的函数可以避免这种情况;但是,必须记住默认类型促销会发生(float会提升为double,而小于int的所有整数类型都会提升为int或{ {1}})。考虑这个示例程序:

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; } 验证__builtin_types_compatible_p(typeof(x), double) && __builtin_types_compatible_p(typeof(y), double)x的{​​{1}}类型。{/ 1>

编译运行时,上述程序将输出

y

在32位x86 Linux(ILP32)以及x86-64(LP64)上进行了测试。是的,上面的程序会泄漏内存,因为它永远不会double 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 } 函数变量返回的动态分配的字符串。

答案 2 :(得分:0)

我发现了一种似乎有效的方法,但是我在编译时仍会收到一些警告......

工作代码:

#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;
}

输出:

int: 1
char*: thing

编译器警告:

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

答案 3 :(得分:0)

在某种程度上可以使用函数指针和union中的无名结构。这里有一个例子,我们重载add和mul函数。有两个工会LIBI,LIBF包含无名结构。 LIBI包含函数指针add和mulc,它只使用整数值。 LIBF与LIBI相同,只是add和mul使用float变量。此外,我们需要在这些联合之外创建addi,muli,addf和mulf函数。联合中的函数指针将被引用到这4个函数中。例如,add in LIBI被称为addi,因为addi使用int值,并且add in LIBF被称为addf,因为它只使用float变量。此示例还可以用作在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; 
 }