C中是否有C语言模板的等价物?

nun*_*nos 16 c

在我写的代码中我需要foo(int, char*)foo(int, int)功能.

如果我用C++编写代码,我会使用模板.C有什么等价物吗?或者我应该使用void指针?怎么样?

Gre*_*ill 29

我认为C中最接近模板的是一些丑陋的宏代码.例如,要定义一个返回其参数两倍的简单函数:

#define MAKE_DOUBLER(T)  \
    T doubler_##T(T x) { \
        return 2 * x;    \
    }

MAKE_DOUBLER(int)
MAKE_DOUBLER(float)
Run Code Online (Sandbox Code Playgroud)

请注意,由于C没有函数重载,你必须使用函数的名称来玩耍(上面的两个doubler_intdoubler_float,并且你必须以这种方式调用它们).

printf("%d\n", doubler_int(5));
printf("%f\n", doubler_float(12.3));
Run Code Online (Sandbox Code Playgroud)

  • 另外,跟随你的维护程序员(也可能是你)将不会喜欢这种技术.如果你看到对`doubler_int()`的调用并且不了解宏,你会如何找到该函数?搜索源代码将无法找到它. (9认同)

Ark*_*nez 16

你不能这样做.
在C中没有重载,一个函数,一个名称,您需要使用支持所有需求的类型,例如(void*)

无论是那个还是一个foo_int(int,int)和一个foo_char(int, char*)

  • 我同意.通常的C解决方案由带有类型后缀的函数名组成.着名的例子:atoi,atol,环礁(IIRC)和OpenGL glVector3f,glVector2f,glColor3f,... (4认同)
  • 你不能。如果您事先不知道它是什么,则需要传递另一个参数来区分。但这有点难看,我会实现 `foo_int` 和 `foo_char` (2认同)
  • 该语言没有用于泛型编程的特定工具这一事实并不意味着人们无法克服这一缺陷。 (2认同)

Mir*_*sin 11

就在这里.您可以在C11中使用type-generic表达式:

#include <stdio.h>

void foo_char_ptr(int a, char *b) {
  printf("Called int, char*.\n");
}

void foo_int(int a, int b) {
  printf("Called int, int.\n");
}

#define foo(a, b) _Generic((b), char*: foo_char_ptr, int: foo_int)(a, b)

int main() {
  foo(1, 1);
  foo(1, "foo");
}

// Output:
// Called int, int.
// Called int, char*.
Run Code Online (Sandbox Code Playgroud)


dmc*_*kee 6

其他人讨论了c在超载方面的内在局限性.但请注意,如果您可以推断出需要哪种情况,您可以使用varargs:

#include <stdarg.h>
foo(int, ...);
Run Code Online (Sandbox Code Playgroud)

如果你不能推断它,你可以传递一个额外的参数:

foo(int, char *spec, ...);
Run Code Online (Sandbox Code Playgroud)

where spec告诉函数在后续参数中期望什么.像功能printfscanf家庭一样.实际上,您可能会发现重复使用printf/scanf约定来指定类型很方便,从而使用户不必使用其他迷你语言.


dzs*_*dzs 5

除了 void*,您还可以使用联合来保存您需要的任何类型的数据:

typedef struct {
    int type;
    union {
        char* char_ptr;
        int int_val;
        // etc...
    };
} foo_data;

void foo(foo_data data)
{
    switch (data.type) {
        case 0:
            printf("%s\n", data.char_ptr);
            break;
        case 1:
            printf("%i\n", data.int_val);
            break;
    }
}

void main()
{
    foo_data data;

    data.type = 0; data.char_ptr = "hello";
    foo(data);
    data.type = 1; data.int_val  = 12;
    foo(data);
}
Run Code Online (Sandbox Code Playgroud)

当然,您应该为类型值创建常量。


Cal*_*ius 5

可以使用模板头实现模板。

让foo.h像这样:

#ifndef PREFIX
    #define PREFIX
#endif
#define CCAT2(x, y) x ## y
#define CCAT(x, y) CCAT2(x, y)
#define FN(x) CCAT(PREFIX, x)

#ifndef T
    #error Template argument missing.
#endif

void FN(foo)(int x, T t)
{
    // Whatever.
}


#undef T
#undef PREFIX
#undef CCAT2
#undef CCAT
#undef FN
Run Code Online (Sandbox Code Playgroud)

要使用它,您可以执行以下操作:

#define T char*
#define PREFIX pchar_
#include "foo.h"

#define T int
#define PREFIX int_
#include "foo.h"
Run Code Online (Sandbox Code Playgroud)

现在您拥有了pchar_foo()并且int_foo()可以使用。

这样做的好处是,如果存在构建问题,则可以在模板标头中获得行号,而不是仅在编译器中说宏错误,并且在某些IDE中代码完成也可以。

PREFIX,在CCATFN宏是很常见的,所以我提取其定义成一个单独的头,和它们的undefinition到另一个。

我使用此模式实现了STL的部分乐趣,并在我的一些C项目中使用了它。

  • 聪明,我喜欢您不只一次添加.h。最佳答案 (2认同)