自定义释放函数是否应考虑与通用容器的兼容性?

时间:2012-05-02 16:58:23

标签: c containers void-pointers generic-programming

如果你想拥有通用容器,在C中,一种流行的方法是使用void*。如果通用容器包含一些具有自己的释放函数的自定义结构,则可能会要求该函数:

struct Foo {...};
Foo *Foo_Allocate(...);
void Foo_Deallocate(const Foo*);


int main(void)
{
    /* Let's assume that when you create the list you have to
       specify the deallocator of the type you want to hold */
    List *list = List_Allocate(Foo_Deallocate);

    /* Here we allocate a new Foo and push it into the list.
       The list now has possession of the pointer. */
    List_PushBack(list, Foo_Allocate());

    /* When we deallocate the list, it will also deallocate all the
       items we inserted, using the deallocator specified at the beginning */
    List_Deallocate(list);
}

但很可能deallocator函数的类型需要void*

typedef void (*List_FnItemDeallocator)(const void*);

问题是Foo_Deallocate需要const Foo*,而不是const void*。传递函数是否仍然安全,即使它们的签名不匹配?可能不是,因为指针类型在C中的大小不一定相同。

如果那是不可能的,那么让所有的deallocator函数都使用const void*而不是指向与它们相关的类型的指针,以便它们与通用容器兼容是不是一个好主意?

2 个答案:

答案 0 :(得分:0)

正如您所说,指向不同函数类型的指针无效。

您应该将void*作为参数,并在每个函数内部执行一些检查以查看给定指针是否与预期类型匹配(例如检查结构开头的幻数)。

答案 1 :(得分:0)

如上所述,您可以使用幻数或“标题”来指定析构函数。你可以使用这个标题走得很远,甚至选择一个“众所周知的,已注册的”deallocator(在这种情况下你实际上不需要存储一个函数指针,可能只是一个整数索引到一个数组中),或者有一个'标题中的flags'部分指定它包含'扩展'解除分配器。可能性非常有趣。

所以你的列表'标题'看起来像这样

#define LIST_HEAD struct list *next; struct list *prev; short flags;
struct list { LIST_HEAD };
struct list_with_custom_deallocator { LIST_HEAD void (*dealloc)(void*); };

现在,实际上回答了你的问题..为什么不只是定义一个公共的头类型,并让你的deallocators指向那个类型的指针(在我的例子中,struct list*)和然后把它投射到任何特定的相关类型 - 或者甚至更好,也可以启发式地确定flags的实际结构和解除分配器(如Binyamin暗示的那样)。