c - C 数据库中的通用查找函数

标签 c database generic-programming void-pointers

所以我尝试使用哈希表在 C 中创建一个数据库(其中我的哈希表只是一个带有 void 指针的 LinkedList 数组)。我还尝试使用 void 指针使其成为“通用”,但我不知道如何使用 void 指针进行查找和删除。例如,我有一个名为 CSG(类(class) StudentID Grade)的元组,定义为

typedef struct CSG {
  char Course[6];
  int StudentId;
  char Grade[2];
  int (*hash)(int StudentId);
}CSG;

其中函数指针仅指向我编写的一个简单的哈希函数。

目前CSG的查找功能为

LinkedList*  lookup_CSG(hashtable* db,int StudentId, char* Grade, char* Course){
    LinkedList* ret=new_LinkedList();
    if(StudentId!=0){
        ListNode* curr=db->table[int_hash(StudentId)]->head;
        while(curr!=NULL){
            CSG* currentCSG=(CSG*)curr->data;
            if(currentCSG->StudentId==StudentId){
                append(ret, currentCSG);
                return ret;
            }
            else{
                curr=curr->next;
            }
        }
    }
    else if(Grade[0]!='*'&&Course[0]!='*'){
        for(int i=0;i<1009;i++){
            ListNode* curr=db->table[i]->head;
            if(curr==NULL){
                continue;
            }
            while(curr!=NULL){
                if(curr==NULL){
                    break;
                }
                CSG* currentCSG=(CSG*)curr->data;
                if(currentCSG==NULL){
                    break;
                }
                if(strcmp(Grade, currentCSG->Grade)==0&&strcmp(Course, currentCSG->Course)==0){
                    append(ret, currentCSG);
                }
                curr=curr->next;
            }
        }
    }
    else if(Grade[0]!='*'){
        for(int i=0;i<1009;i++){
            ListNode* curr=db->table[i]->head;
            if(curr==NULL){
                continue;
            }
            while(curr!=NULL){
                if(curr==NULL){
                    break;
                }
                CSG* currentCSG=(CSG*)curr->data;
                if(currentCSG==NULL){
                    break;
                }
                if(strcmp(Grade, currentCSG->Grade)==0){
                    append(ret, currentCSG);
                }
                curr=curr->next;
            }
        }
    }
    else if(Course[0]!='*'){
        for(int i=0;i<1009;i++){
            ListNode* curr=db->table[i]->head;
            if(curr==NULL){
                continue;
            }
            while(curr!=NULL){
                if(curr==NULL){
                    break;
                }
                CSG* currentCSG=(CSG*)curr->data;
                if(currentCSG==NULL){
                    break;
                }
                if(strcmp(Course, currentCSG->Course)==0){
                    append(ret, currentCSG);
                }
                curr=curr->next;
            }
        }
    }
    return ret;
}

其中 "* "0 字符用于省略某个参数,即如果您调用 lookup_CSG(db,0 "A-","* ") 将返回所有等级为 A- 的元组的链表。

这工作得很好,但是对于这个模型,我必须为每个元组编写一个特定的查找和删除函数。有谁知道是否有一种方法可以创建“通用”查找函数。当我尝试这样做时,我遇到了麻烦,因为每个元组都有不同的属性,因此比较会有所不同,铸件也会有所不同。查找函数还将根据元组采用不同的参数。谢谢!

最佳答案

有两种方法可以解决这个问题:

  1. 对所有值类型和参数使用 void*,或者
  2. 编写一堆宏,为所有输入类型生成强类型哈希表(就像 klib 中所做的那样)。

对于第一种方法,您将需要两个接受 void* 的函数指针,可以使用以下方法对其进行类型定义:

// returns a 32-bit hash value for the specified key
typedef int32_t (*HashGetterFn)(const void*);

// return 'true' if two parameters are equal
typedef bool (*EqualityFn)(const void*, const void*);

// you can also place these in a struct
typedef struct 
{
    HashGetterFn getHash;
    EqualityFn equals;
}
HashFuncs;

这允许您对您喜欢的任何键使用相同的函数签名,尽管您必须在函数中通过引用传递它。

因此,如果您的 key 是 int,您将编写这两个函数:

// a hash implementation for int keys
static int32_t int_hash(const void * input)
{
     // cast the void pointer to the actual key pointer
     const int * value = (const int *)input;

     // dereference it and calculate the hash
     return (int32_t)*value;
}

static bool int_equals(const void * a, const void * b)
{
     // cast the void pointers to actual key pointers
     const int * aval = (const int *)a;
     const int * bcal = (const int *)b;

     // dereference and compare
     return *aval == *bval;
}

// and then you use it somewhere
void some_function(int *item)
{
    // wrap these two functions
    HashFuncs hashfn = { .getHash = int_hash, .equals = int_equals };

    // create a hashtable which will use them 
    HashTable hashtable = hashtable_create(hashfn);

    // add a key/value pair
    hashtable_add(&hashtable, (void*)item);
}

您可能已经注意到两个问题:

  1. 您的值需要具有静态持续时间或在堆上分配,因为它们是通过引用传递的。或者,您还需要将结构体的大小传递给哈希表,并让其所有函数将每个结构体实例的 memcpy 到内部表中。

  2. 没有什么可以阻止您向哈希表函数传递完全错误的值,因为它们接受 void 指针以允许它们处理任何类型。这意味着当您的哈希函数之一取消引用错误的指针类型时,编译没有问题的函数可能会在运行时失败。

为了缓解这种情况,klib 采取的方法是使用预处理器为要使用的每个特定键/值对生成强类型函数和结构的列表。这种方法也并不完美;编写和测试大量的东西是很痛苦的multi-line macros ,但由于这个库已经编写完毕,您不妨重用它。

这基本上是泛型编程在许多现代语言中优雅修复的问题之一(C++ 中的模板、C# 或 Java 中的泛型)。

关于c - C 数据库中的通用查找函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58885191/

相关文章:

mysql - 使用查询更新 mysql 数据库文本

arrays - 从单例创建一个通用数组

c++ - 泛型函数的重载可以对其他重载开放吗?

php - 在 Drupal 上连接数据库表

c++ - 将模板类的对象作为需要基模板类的函数参数传递

c - visual studio 2012无法编译用VS 2013制作的解决办法

c - 相同的 srand 种子在不同的计算机上产生不同的值

c - 如何使用指针遍历结构中的内存块?

c - 如何使用简单的转义序列对东欧(波兰)符号进行编码?

ruby-on-rails - 我应该在 Rails 中创建用于存储用户地址的新表吗