我正在尝试将用Java编写的库移植到C编程语言中。对于Java接口,我打算使用函数指针的结构来替换,例如:
// Java code
public interface ActionsFunction {
Set<Action> actions(Object s);
}
/* C code */
typedef struct ActionsFunction {
List* (*actions)(void* s);
void (*clear_actions)(struct List **list); /* Since C doesn't have garbage collector */
} ActionsFunction;
我的问题是:它是否是一个合适的解决方案,我如何模拟通用接口,如:
public interface List <E> {
void add(E x);
Iterator<E> iterator();
}
更新:
我还必须面对另一个问题:实现通用的抽象数据结构,如List,Queue,Stack等,因为C标准库缺乏这些实现。我的方法是客户端代码应该传递其数据的指针及其大小,从而允许库保存那个而不指定其类型。再一次,这只是我的想法。我需要你的设计建议和实施技术。
我的初始移植代码可在以下位置找到: https://github.com/PhamPhiLong/AIMA
通用抽象数据结构可以在实用程序子文件夹中找到。
答案 0 :(得分:4)
这是一个非常简短的例子,使用宏来完成这样的事情。这可能会很快变得毛茸茸,但如果操作正确,您可以保持完整的静态类型安全。
#include <stdlib.h>
#include <stdio.h>
#define list_type(type) struct __list_##type
/* A generic list node that keeps 'type' by value. */
#define define_list_val(type) \
list_type(type) { \
list_type(type) *next; \
type value; \
}
#define list_add(plist, node) \
do \
{ \
typeof(plist) p; \
for (p = plist; *p != NULL; p = &(*p)->next) ; \
*p = node; \
node->next = NULL; \
} while(0)
#define list_foreach(plist, p) \
for (p = *plist; p != NULL; p = p->next)
define_list_val(int) *g_list_ints;
define_list_val(float) *g_list_floats;
int main(void)
{
list_type(int) *node;
node = malloc(sizeof(*node));
node->value = 42;
list_add(&g_list_ints, node);
node = malloc(sizeof(*node));
node->value = 66;
list_add(&g_list_ints, node);
list_foreach(&g_list_ints, node) {
printf("Node: %d\n", node->value);
}
return 0;
}
答案 1 :(得分:2)
在C中进行泛型编程有一些常用方法。我希望在尝试完成您所描述的任务时使用以下一种或多种方法。
MACROS:一种是使用宏。在这个例子中,MAX看起来像一个函数,但是对任何可以与&#34;&gt;&#34;进行比较的东西进行操作。操作者:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
int i;
float f;
unsigned char b;
f = MAX(7.4, 2.5)
i = MAX(3, 4)
b = MAX(10, 20)
VOID * :另一种方法是使用void *
指针来表示通用数据,然后将函数指针传递给算法以对数据进行操作。查找<stdlib.h>
函数qsort
以获取此技术的典型示例。
UNIONS :另一种技术是使用工会来保存多种不同类型的数据。这使得对数据进行操作的算法有点难看,但可能无法节省大量编码:
enum { VAR_DOUBLE, VAR_INT, VAR_STRING }
/* Declare a generic container struct for any type of data you want to operate on */
struct VarType
{
int type;
union data
{
double d;
int i;
char * sptr;
};
}
int main(){
VarType x;
x.data.d = 1.75;
x.type = VAR_DOUBLE;
/* call some function that sorts out what to do based on value of x.type */
my_function( x );
}
CLEVER CASTING&amp; POINTER MATH 这是一个非常常见的习惯用法,可以看到数据结构的函数在特定类型的结构上运行,然后要求结构中包含的结构可以做任何有用的事情。
执行此操作的简单方法是,允许插入数据结构的结构成为派生类型的第一个成员。然后你可以无缝回放&amp;在两者之间。更通用的方法是使用&#39; offsetof&#39;。这是一个简单的例子。
例如:
/* Simple types */
struct listNode { struct listNode * next; struct listNode * prev };
struct list { struct listNode dummy; }
/* Functions that operate on those types */
int append( struct list * theList, struct listNode * theNode );
listNode * first( struct list *theList );
/* To use, you must do something like this: */
/* Define your own type that includes a list node */
typedef struct {
int x;
double y;
char name[16];
struct listNode node;
} MyCoolType;
int main() {
struct list myList;
MyCoolType coolObject;
MyCoolType * ptr;
/* Add the 'coolObject's 'listNode' member to the list */
appendList( &myList, &coolObject.node );
/* Use ugly casting & pointer math to get back you your original type
You may want to google 'offsetof' here. */
ptr = (MyCoolType *) ( (char*) first( &myList )
- offsetof(MyCoolType,node);
}
libev文档中有一些关于最后一种技术的更好例子:
http://search.cpan.org/dist/EV/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_(OR_BOTH)