【问题标题】:generalizing linked list for different Node types不同节点类型的泛化链表
【发布时间】:2018-08-14 04:38:02
【问题描述】:

我有两种或多种不同类型的结构,它们使用它们自己类型的节点的链表,我注意到检索、删除、插入和检查节点是否存在的代码在所有结构中都是相同的当代码被重复时。有没有更好的办法?

猫屋

typedef struct Cat Cat;
struct Cat {};

typedef struct CatNode CatNode;
struct CatNode {
    char *name;
    Cat *cat;
    CatNode *next;
};

typedef struct CatHouse CatHouse;
struct CatHouse {
    CatNode *cats;
    Proxy *(*getCat)(const CatHouse *self, const char *name);
    bool (*hasCat)(const CatHouse *self, const char *name);
    Proxy *(*remove)(CatHouse *self, const char *name);
};

狗屋

typedef struct Dog Dog;
struct Dog {};

typedef struct DogNode DogNode;
struct DogNode {
    char *name;
    Dog *dog;
    DogNode *next;
};

typedef struct DogHouse DogHouse;
struct DogHouse {
    DogNode *dogs;
    Dog *(*getDog)(const DogHouse *self, const char *name);
    bool (*hasDog)(const DogHouse *self, const char *name);
    Dog *(*remove)(DogHouse *self, const char *name);
};

这是在两个实体中重复的部分实现。

如果重复是如何完成的,我很好,我也想知道如何在商业项目中处理链表,因为它们有多个实体结构,每个都有自己的链表,而且可能不止一个?

static bool hasCat(const Cat *self, const char *name) {
    CatNode *cursor = self->cats;
    while (cursor != NULL && strcmp(cursor->name, name) != 0)
        cursor = cursor->next;
    return cursor != NULL;
}


static bool hasDog(const Dog *self, const char *name) {
    DogNode *cursor = self->dogs;
    while (cursor != NULL && strcmp(cursor->name, name) != 0)
        cursor = cursor->next;
    return cursor != NULL;
}

除了类型不同之外,其余的功能也类似地重复。

【问题讨论】:

  • 一种方法是使用大量宏:)

标签: c linked-list


【解决方案1】:

你可以使用 C 的泛型来获得一个可以容纳任何你想要的东西的链表。

一种方法是这样的:

typedef struct link {
    void        *data;
    struct link *previous;
    struct link *next;
} link_s;

typedef struct list {
    link_s *head;
    link_s *tail;
    size_t nbLink;

    /* function pointer */
    int    (*Data_Compare)(const void *data1, const void *data2);
    void   (*Data_Destructor)(void *data);
} list_s;

然后,您必须提供一个函数,用于知道哪些“数据”低于、等于或优于另一个(Data_Compare 函数指针,作用类似于 strcmp),并且您可以提供一个函数来“销毁”您的数据(例如,您进行了内存分配)。

之后,您可以将“数据”作为“猫”和“狗”结构的并集,从而允许一个链表容纳所有,或者您可以只有两个链表,一个用于“ cat”,另一个用于“dog”(请注意为两者提供良好的 Data_Compare)。

在我的实现中,我提供了以下函数来操作 list_s :

void List_Constructor(list_s *self, int (*Data_Compare)(const void *data1, const void *data2), void (*Data_Destructor)(void *data));
void List_Destructor(list_s *self);

bool List_Add(list_s *self, void *data);

void *List_RemoveByLink(list_s *self, link_s *link);
void *List_RemoveByData(list_s *self, void *data);
void *List_RemoveByCondition(list_s *self, bool (*Data_Condition)(const void *data));

void List_DestroyByLink(list_s *self, link_s *link);
/* Delete all the link corresponding to data */
void List_DestroyByData(list_s *self, void *data);
/* Delete all the link which condition is true */
void List_DestroyByCondition(list_s *self, bool (*Data_Condition)(const void *data));

void List_Sort(list_s *self);
void List_Merge(list_s *to, list_s *from);
void List_Reverse(list_s *self);

编辑:

为了在下面的例子中拥有线程安全功能,你可以做这样的事情:

typedef struct ts_list {
  list_s          list;
  pthread_mutex_t mutex;
} ts_list_s;

void TsList_Constructor(ts_list_s *self, int (*Data_Compare)(const void *data1, const void *data2), void (*Data_Destructor)(void *data));
void TsList_Destructor(ts_list_s *self);

bool TsList_Add(ts_list_s *self, void *data);

void *TsList_RemoveByLink(ts_list_s *self, link_s *link);
void *TsList_RemoveByData(ts_list_s *self, void *data);
void *TsList_RemoveByCondition(ts_list_s *self, bool (*Data_Condition)(const void *data));

void TsList_DestroyByLink(ts_list_s *self, link_s *link);
void TsList_DestroyByData(ts_list_s *self, void *data);
void TsList_DestroyByCondition(ts_list_s *self, bool (*Data_Condition)(const void *data));

void TsList_Sort(ts_list_s *self);
void TsList_Merge(ts_list_s *to, ts_list_s *from);
void TsList_Reverse(ts_list_s *self);

// Addtionnal function, for comfort
bool TsList_LockMutex(ts_list_s *self);
bool TsList_UnlockMutex(ts_list_s *self);

所有 TsList_* 函数看起来像:

bool TsList_Add(ts_list_s *self, void *data)
{
  bool returnFunction;

  if (!TsList_LockMutex(self)) {
    return (false);
  }

  returnFunction = List_Add(&self->list, data);

  if (!TsList_UnlockMutex(self)) {
    // Big critical log, because from this point, there will be deadlock
  }
  return (returnFunction);
}

当然,您必须非常警惕死锁,尤其是使用“List_Merge”功能,因为您必须在合并之前锁定两个列表。

【讨论】:

  • 您可以提供任何线程安全的单个函数,或者使用 hasCat 函数使其线程安全?
  • 事实上,这不是线程安全的,也不是设计可重入的。如果您想要线程安全,则必须将 list_s 变量与互斥锁“关联”,并对所有 List_* 函数进行线程安全函数调用。我不知道我是否清楚,英语不是我的母语。
  • 没问题,我可以根据您的评论看一些代码
  • 我已经编辑了我的答案,这是你要找的吗?
  • 所以,是的,只需制作两个链表,一个用于狗,另一个用于猫。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多