ANSI C中的通用容器

时间:2012-07-25 20:41:14

标签: c

由于几个对这个问题不重要的原因,是否可以为ANSI中的一般结构实现一个列表 - 容器?这个列表可以包含不同类型的结构吗?

编辑:经过几次建议,我正在考虑一些选择。但我仍然不清楚为什么没有标准的方法来做到这一点。在ANSI C中没有必要吗?唯一的方法是使用另一种语言?

干杯

4 个答案:

答案 0 :(得分:2)

根据您的目的,可能。这是屁股的主要痛苦,但它是可能的。

您可以使用void指针创建通用列表,例如

struct glist {
  void *data;
  stuct glist *next;
};

data成员可以指向任何类型的对象。问题是,一旦指定了指针,就会丢失该类型信息;你需要分别跟踪类型。您可以使用整数或枚举值来标记类型,例如

enum dtype {type_A, type_B, type_C, ...};

struct glist {
  void *data;
  enum dtype data_type;
  struct glist *next;
};

#define TYPE_A 1
#define TYPE_B 2
...
struct glist {
  void *data;
  int data_type;
  struct glist *next;
};

从列表中检索元素时,您需要检查data_type成员以确定要调用哪些函数或根据数据类型执行哪些过程:

switch (elem->data_type)
{
  case TYPE_A: // process for TYPE A
    break;

  case TYPE_B: // process for TYPE B
    break;

  ...
}

或者,您可以存储指向一个或多个对该特定数据类型进行操作的函数的指针,并完全跳过switch或if-else语句:

struct glist {
  void *data;
  void (*process)(const void *);
  void (*copy)(const void *);
  void (*delete)(const void *); // not shown
  struct glist *next;
};

对于每种数据类型,您创建一个过程函数的实例,并将指向该函数的指针绑定到该节点。该函数必须以void *作为输入,但它将转换为适当的类型以进行处理:

void process_typeA (const void *data)
{
  typeA *obj = (typeA *) data; // cast away const
  // process obj as necessary
}

void *copy_typeA (const void *data)
{
  typeA *obj = (typeA *) data;
  typeA *newObj = malloc(sizeof *newObj);
  if (newObj)
    // copy from obj to newObj
  return newObj;
}

void addItem (struct glist *list, const void *obj, 
              void (*process)(const void *), 
              void *(*copy)(consg void *),
              void (*delete)(const void *))
{
  struct glist *newNode = malloc(sizeof *newNode);
  newNode->copy = copy;
  newNode->delete = delete;
  newNode->process = process;
  newNode->data = (*copy)(obj);
  newNode->next = NULL;

  // add newNode to list 
}

void foo(void)
{
  struct glist *myList = new_list(); // not shown here
  ...
  addItem(myList, myTypeAObj, process_typeA, copy_typeA, delete_typeA);
  addItem(myList, myTypeBObj, process_typeB, copy_typeB, delete_typeB);
  ...
}

然后,当您浏览列表时,可以调用适合该特定节点的process函数:

void processList(struct glist *list)
{
  struct glist *node = list;
  while (node)
  {
    node->process(node->data);
    node = node->next;
  }
  ...
}

此方法的优点在于您将该节点的行为与节点本身相关联;您不需要继续将案例添加到交换机或if-else链。缺点是你正在使用 lot 指针,你必须为你想要使用的每种数据类型实现一个单独的进程/复制/删除函数,你做的是非微不足道的内存管理(你需要复制和删除方法,原因一旦你想到它就很明显),并且你把任何类型安全的概念抛到了窗外并进入迎面而来的流量;您将失去任何编译器级别的保护,以防止错误的进程/复制/删除功能与错误的数据类型相关联。

答案 1 :(得分:1)

您可能想要阅读Linux内核链接列表。 (http://isis.poly.edu/kulesh/stuff/src/klist/有一些信息)

它可以存储任意结构,但我相信给定列表中只有一种类型。自从我使用它已经有一段时间了,所以我对细节有点生疏。正如理查德所说,它使用了一些预处理器伏都教,但不是太多。

答案 2 :(得分:0)

雅各布·纳维亚(Jacob Navia)有“The C Containers library project”的计划。

答案 3 :(得分:0)

你看过sglib吗?

http://sglib.sourceforge.net/