我正在用 C 编写一个链表实现,并希望使用与 C++模板等效的语言功能来简化我的工作。
有这样的功能吗?
C has no templates like C++, though you can achieve something similar with "clever" (or WTFey, depending on how you look at it) use of #define
macros.
However, take a look at how for example GLib does it for singly linked lists or doubly linked lists.
模板是 C++ 的特性,但如果您想要单链表或双链表的类型无关实现,可以借助宏来实现,或者您可以简单地将void*
指针存储在结构中。
当然,互联网上有很多这样的实现。@MohamedKALLEL和@hyde已经给出了来自 Linux 内核和 GLib 的示例,我只想添加一个关于漂亮的小库uthash的注释。
它在 C 中实现了一个哈希表,但它也有一个utlist.h ,它完全在宏上实现单链和双链(甚至循环)列表。也就是说,您可以简单地获取此文件,将其包含在内,然后按原样使用这些宏,或者根据需要对其进行修改。同样好的是您可以使用任何数据结构:它只需要有next
指针(并且prev
,如果是双重链接的)。
Ps 但是在使用宏时要记住:能力越大,责任越大。宏很强大,但可能会变得非常不安全和不可读。谨防!
如果您make
用来构建您的软件,您可以用来实现类似结果的一种方法是让 make 根据您编写的模板生成您的代码,方法是调用 sed 或 awk 之类的东西。我已经多次使用这种方法,虽然它缺乏 C++ 模板提供的灵活性和功能,但它非常透明(与宏不同),构建非常高效,并且除了久经考验的老式 unix 实用程序外,不需要添加任何工具(例如make
和sed
)。主要缺点是您将在与代码不同的位置(Makefile中的一行)指定查找/替换字符串。
请注意,通常情况下,使用函数指针和 void 指针来编写一个通用的代码库会更好。但是,如果您希望通过消除不必要的函数调用来获得额外的性能,例如在紧密循环中,模板可能会更好,并且可以模拟如下:
使用占位符名称编写模板代码,并使用诸如code.template.c 之类的模板名称
在替换为适当的名称后,使用对模板代码的函数调用编写非模板代码,例如my_int_func()
或my_string_func()
可能,使用#include 将您的模板代码包含在您的非模板代码中(例如,如果您的模板将具有inline
功能)
将您的 Makefile 写入:
sed
是一个很好的替代工具,但您也可以使用 egreplace
或awk
或其任何 Windows 等效工具例如:
代码模板.c
/* Makefile will call sed to replace DATANAME, DATATYPE and SPECIFIER */
void print_DATANAME_data(DATATYPE x) {
printf("%SPECIFIER\n", x);
}
代码.c
#include <stdio.h>
#include "printfuncs.generated.c"
int main() {
int i = 99;
print_int_data(99);
char *s = "hello";
print_str_data(s);
float f = 1.234;
print_float_data(f);
}
生成文件
all: my_program
my_program: code.c
CC -o $@ code.c
code.c: printfuncs.generated.c
printfuncs.generated.c: code.template.c
rm -f printfuncs.generated.c
cat code.template.c | sed 's/DATANAME/int/g;s/DATATYPE/int/g;s/SPECIFIER/i/g;' >> printfuncs.generated.c
cat code.template.c | sed 's/DATANAME/str/g;s/DATATYPE/char */g;s/SPECIFIER/s/g;' >> printfuncs.generated.c
cat code.template.c | sed 's/DATANAME/float/g;s/DATATYPE/float/g;s/SPECIFIER/f/g;' >> printfuncs.generated.c
建造
make
这将生成 printfuncs.generated.c(如果该文件不存在或修改时间少于 code.template.c),如下所示:
/* Makefile will call sed to replace int, int and i */
void print_int_data(int x) {
printf("%i", x);
}
/* Makefile will call sed to replace str, char * and s */
void print_str_data(char * x) {
printf("%s", x);
}
/* Makefile will call sed to replace float, float and f */
void print_float_data(float x) {
printf("%f", x);
}
任何编译器错误都将指向此文件,您可以直接修改此文件以使其编译(之后您需要更新模板以防止您的更改丢失),或编辑模板。无论哪种情况,在您编辑之后,您需要做的就是make
再次运行(尝试)编译。
跑
./my_program
(或者my_program.exe
如果建立在胜利之上)
嗨,我不知道链表,但是对于模板函数,您可能可以使用宏或具有不同数量参数的函数作为该程序的示例
#include <stdarg.h>
#include <stdio.h>
#define INT 0
#define STR 1
void foo( int type, ... )
{
va_list ap;
int i;
char *s;
va_start( ap, type );
switch( type ) {
case INT:
i = va_arg( ap, int );
printf( "INT: %i\n", i );
break;
case STR:
s = va_arg( ap, char * );
printf( "STR: %s\n", s );
break;
default:
break;
}
va_end( ap );
}
#define SWAP( type, a, b ) { \
type t; \
t = a; \
a = b; \
b = t; \
}
int main( void )
{
foo( INT, 3 );
foo( STR, "baz" );
int ia = 0, ib = 3;
SWAP( int, ia, ib );
printf( "%i %i\n", ia, ib );
float fa = 0.5, fb = 3.14;
SWAP( float, fa, fb );
printf( "%f %f\n", fa, fb );
return 0;
}
将产生输出
INT: 3
STR: baz
3 0
3.140000 0.500000
看看这篇文章。
C 没有静态模板,但您可以使用宏来模拟它们。
// define a macro
#define DEF(type, name, val) type name = val
// call the macro
DEF(int, foo, 5);
// print the called macro
printf("%d", foo);
“这个模板允许定义具有基本类型的变量所需的代码被概括和抽象。目标是使代码在类型之间可共享和相似。然而,这个例子是微不足道的,因为它只提供了很少的简化写出声明。真正的力量来自执行更复杂任务的代码。
#include <stdio.h>
int main()
{
#define FOREACH(type, start, end, fn) \
for (type _foreach_var = start; _foreach_var != end; _foreach_var++) \
{ \
fn(_foreach_var); \
}
#define PRINT_INT(n) printf("%d\n", n)
// use FOREACH
FOREACH(int, 0, 5, PRINT_INT)
}
FOREACH(int, 0, 5, PRINT_INT)