Как создать универсальную функцию C

#c #generics #struct #insert #hashmap

#c #дженерики #структура #вставить #hashmap

Вопрос:

Я должен создать HashMap. Я создал две структуры. A node struct и a table struct . Вторая структура будет «содержать» мои узлы. Моя проблема в insert функции: я должен вставить в свою таблицу (* t) пару (ключ, значение). Проблема в том, что тип ключа и значения должны быть универсальными. Итак, я хотел бы вставить int, double, char, ecc.. Как я могу превратить свою функцию в универсальную функцию?

insert.c

 struct node{
    int key;
    int val;
    struct node *next;
    struct node *prev;
};
struct table{
    int size;
    struct node **list;
};

int hashCode(int key){
    if(key>0)
        return key-1;
    return key 1;
}

void insert(struct table *t,int key,int val){
    int pos = hashCode(key);
    struct node *list = t->list[pos];
    struct node *newNode = (struct node*)malloc(sizeof(struct node));
    struct node *temp = list;
    while(temp){
        if(temp->key==key){
            printf("%s", "Key already created");
            return;
        }
        temp = temp->next;
    }
    newNode->next = list;
    newNode->key = key;
    newNode->val = val;
    if(list!=NULL){
        list->prev = newNode; 
    }
    t->list[pos] = newNode;
    newNode->prev = NULL;
}
 

main.c

 int main(){
    struct table *t = /* I create the structure */ ;
    insert(t,2,3);
    insert(t,3,4);
    insert(t,2,3);
    // insert(t,'a','c');  function insert should execute also with char, int, float, double ecc...
    return 0;
}
 

Комментарии:

1. Вы можете использовать a union для хранения ключей и значений, а enum также для указания того, какие данные хранятся.

2. Я никогда не использовал union и enum, как я могу использовать их в своей функции? @jha-G

3. Вы можете создать универсальную реализацию хэша, которой предоставляется функция сравнения для ключа, и она сохраняет ключ как тип данных void (или указатель на void).

4. Параметры вставки являются Int, так как я могу передать символ, например? Мне нужно передать ей весь тип, а не только int. @PaulOgilvie

5. вы также можете использовать void * for key и value и добавить дополнительный тип параметра (можно использовать enum ) в свой node для декодирования для исправления типа, когда это необходимо

Ответ №1:

Вы определяете объединение следующим образом :

 union value {
    int i;
    float f;
    char c;
//and so on
};
 

И ваш узел, подобный этому :

 struct node{
    int key;
    union value val;
    struct node *next;
    struct node *prev;
};
 

Вы меняете аргумент своей функции следующим образом :

 void insert(struct table *t, int key, union value val) {}
 

И вашим основным становится (я использую макрос для упрощения создания объединений) :

 #define UVAL(v, k) (union value){ .k = v}

int main(){
    struct table * t = NULL; //Todo

    insert(t, 2, UVAL(3, i) );
    insert(t, 3, UVAL(4, i));
    insert(t, 2, UVAL(3, i));
    return 0;
}
 

С некоторыми поплавками вместо :

 int main(){
    struct table * t = NULL; //Todo

    insert(t, 2, UVAL(3.5f, f));
    insert(t, 3, UVAL(4.f,  f));
    insert(t, 2, UVAL(3.5f, f));
    return 0;
}
 

Комментарии:

1. Спасибо! Могу ли я спросить вас, что {.k = v} означает в определении?

2. k представляет имя поля в объединении. Здесь я передаю i или f, поэтому после расширения макроса он станет .i или .f

Ответ №2:

Начиная с C11, вы можете использовать _Generic

 #include <stdio.h>

#define hashCode(x) _Generic((x), 
    int: hash_int, 
    double: hash_double 
)(x)

int hash_int(int value)
{
    printf("Hashing %dn", value);
    return 42;
}

int hash_double(double value)
{
    printf("Hashing %fn", value);
    return 42;
}

int main(void)
{
    hashCode(3);
    hashCode(3.14);
    return (0);
}
 

Из cppreference

Объяснение

Предоставляет способ выбора одного из нескольких выражений во время компиляции на основе типа управляющего выражения

Во-первых, тип управляющего выражения подвергается преобразованиям lvalue. Преобразование выполняется только в домене типов: оно отбрасывает cvr-квалификаторы верхнего уровня и атомарность и применяет преобразования массива в указатель / функции в указатель к типу управляющего выражения, не инициируя никаких побочных эффектов или вычисляя какие-либо значения.

Тип после преобразования сравнивается с именами типов из списка ассоциаций.

Если тип совместим с именем типа одной из ассоциаций, то тип, значение и категория значения универсального выбора — это тип, значение и категория значения выражения, которое появляется после двоеточия для этого имени типа.

Если ни одно из имен типов не совместимо с типом управляющего выражения, и предоставляется ассоциация по умолчанию, то тип, значение и категория значения универсального выбора являются типом, значением и категорией значения выражения после метки default : .