2015-07-06 3 views
0

В Ubuntu 14.04, я написал C файл под названием hash.c:Python ctypes: OSError неопределенный символ при загрузке библиотеки

/* hash.c: hash table with linear probing */ 

typedef struct { 
    void *key; 
    void *value; 
} ht_entry; 

typedef struct { 
    ht_entry *table; 
    int len; 
    int num_entries; 
    int (*hash_fn)(void *key); 
    int (*key_cmp)(void *k1, void *k2); 
} hashtable; 

и скомпилирован с

gcc -shared hash.c -o test.so -fPIC 

После этого я попытался загрузить test.so в скрипте Python (для тестирования), но я получил следующую ошибку: «OSError: .../test.so: undefined symbol: hash_fn»

hash_fn - это указатель на функцию в структуре hashtable. Он ссылается несколько раз на функции позже в файле.

Я не понимаю, почему эта ошибка происходит. У меня есть Googled, но все остальные случаи относятся либо к C++, либо к ним. В моем случае у меня есть только 1 файл C, который включает только stdio и stdlib.


вот код FULL. Когда я комментирую все, кроме hash_create и print_info, он загружается успешно. Когда я раскомментирую find(), это ошибка. (print_info только для тестирования, что ctypes работы)

/* hash.c: hash table with linear probing */ 

#include <stdio.h> 
#include <stdlib.h> 

typedef struct { 
    void *key; 
    void *value; 
} ht_entry; 

typedef struct { 
    ht_entry *table; 
    int len; 
    int num_entries; 
    int (*hash_fn)(void *key); 
    int (*key_cmp)(void *k1, void *k2); 
} hashtable; 

static void close_gap(hashtable *ht, int i); 
static int find(hashtable *ht, void *key); 

hashtable* hash_create(int len, int (*hash_fn)(void*), int (*key_cmp)(void*, void*)) 
{ 
    hashtable* ht = (hashtable*) malloc(sizeof(hashtable)); 
    ht->len = len; 
    ht->table = calloc(len, sizeof(ht_entry)); 
    ht->hash_fn = hash_fn; 
    ht->key_cmp = key_cmp; 
    ht->table[0].key = 2; 
    ht->table[0].value = 3; 
    return ht; 
} 

void print_info(hashtable *ht) 
{ 
    printf("%d, %d, %d\n", ht->len, ht->table[0].key, ht->table[0].value); 
} 

void* hash_retrieve(hashtable* ht, void *key) 
{ 
    int i = find(ht, key); 
    if(i < 0) { 
     return NULL; 
    } 

    return ht->table[i].value; 
} 

void hash_insert(hashtable* ht, void *key, void *value) 
{ 
    if(ht->num_entries == ht->len) { 
     return; 
    } 

    int i = hash_fn(key) % ht->len; 
    while(ht->table[i].key != NULL) { 
     i = (i + i) % ht->len; 
    } 
    ht->table[i].key = key; 
    ht->table[i].value = value; 
} 

void hash_remove(hashtable *ht, void *key) 
{ 
    int i = find(ht, key); 
    if(i < 0) { 
     return; 
    } 
    ht->table[i].key = 0; 
    ht->table[i].value = 0; 
    close_gap(ht, i); 
} 

static int find(hashtable *ht, void *key) 
{ 
    int i = hash_fn(key) % ht->len; 
    int num_checked = 0; 
    while(ht->table[i].key && num_checked != ht->len) { 
     if(!ht->key_cmp(ht->table[i].key, key)) { 
      return i; 
     } 
     num_checked++; 
     i = (i + i) % ht->len; 
    } 
    return -1; 
} 

static void close_gap(hashtable *ht, int i) 
{ 
    int j = (i + 1) % ht->len; 
    while(ht->table[j].key) { 
     int loc = ht->hash_fn(ht->table[j].key); 
     if((j > i && (loc <= i || loc > j)) || (j < i && (loc <= i && loc > j))) { 
      ht->table[i] = ht->table[j]; 
      ht->table[j].key = 0; 
      ht->table[j].value = 0; 
      close_gap(ht, j); 
      return; 
     } 
    } 
} 
+0

Похоже, что 'hash_fn' будет глобальной функцией. Имена членов структуры не компилируются в качестве символов в исполняемый файл. –

+0

Нам действительно нужно увидеть строку кода, из которой исходит ошибка. Где и как вы называете 'hash_fn'? – cdarke

+0

@cdarke Готово. См. OP. – user1299784

ответ

2

Когда я использовать строку компиляции я получаю пять предупреждений. Здесь есть несколько проблем. Сначала вы пытаетесь назначить int на номер void * в нескольких местах. Это вызывает предупреждение, и оно вылетает во время выполнения, потому что вы передаете 2 и 3 в качестве адресов.

Во-вторых, вы вызываете hash_fn в нескольких местах вместо ht->hash_fn. Это приводит к ошибке компоновщика, но вы должны рассмотреть мои другие изменения, в противном случае это приведет к краху во время выполнения с SIGSEGV:

/* hash.c: hash table with linear probing */ 

#include <stdio.h> 
#include <stdlib.h> 

typedef struct { 
    void *key; 
    void *value; 
} ht_entry; 

typedef struct { 
    ht_entry *table; 
    int len; 
    int num_entries; 
    int (*hash_fn)(void *key); 
    int (*key_cmp)(void *k1, void *k2); 
} hashtable; 

static void close_gap(hashtable *ht, int i); 
static int find(hashtable *ht, void *key); 


hashtable* hash_create(int len, int (*hash_fn)(void*), int (*key_cmp)(void*, void*)) 
{ 
    hashtable* ht = (hashtable*) malloc(sizeof(hashtable)); 
    ht->len = len; 
    ht->table = calloc(len, sizeof(ht_entry)); 
    ht->hash_fn = hash_fn; 
    ht->key_cmp = key_cmp; 

    // <<< Code changed here 
    /* 
    ht->table[0].key = 2; 
    ht->table[0].value = 3; 
    */ 

    { 
     int *p = malloc(sizeof(int)); 
     *p = 2; 
     ht->table[0].key = p; 

     p = malloc(sizeof(int)); 
     *p = 3; 
     ht->table[0].value = p; 
    } 
    // end of code change 

    return ht; 
} 

void print_info(hashtable *ht) 
{ 
    // <<<< Code changed 
    printf("%d, %d, %d\n", ht->len, 
      *(int *)ht->table[0].key, *(int *)ht->table[0].value); 
} 

void* hash_retrieve(hashtable* ht, void *key) 
{ 
    int i = find(ht, key); 
    if(i < 0) { 
     return NULL; 
    } 

    return ht->table[i].value; 
} 

void hash_insert(hashtable* ht, void *key, void *value) 
{ 
    if(ht->num_entries == ht->len) { 
     return; 
    } 

    // <<< Code changed 
    int i = ht->hash_fn(key) % ht->len; 

    while(ht->table[i].key != NULL) { 
     i = (i + i) % ht->len; 
    } 
    ht->table[i].key = key; 
    ht->table[i].value = value; 
} 

void hash_remove(hashtable *ht, void *key) 
{ 
    int i = find(ht, key); 
    if(i < 0) { 
     return; 
    ht->table[i].key = 0; 
    ht->table[i].value = 0; 
    close_gap(ht, i); 
} 

static int find(hashtable *ht, void *key) 
{ 
    // <<< Code changed 
    int i = ht->hash_fn(key) % ht->len; 

    int num_checked = 0; 
    while(ht->table[i].key && num_checked != ht->len) { 
     if(!ht->key_cmp(ht->table[i].key, key)) { 
      return i; 
     } 
     num_checked++; 
     i = (i + i) % ht->len; 
    } 
    return -1; 
} 


static void close_gap(hashtable *ht, int i) 
{ 
    int j = (i + 1) % ht->len; 
    while(ht->table[j].key) { 
     int loc = ht->hash_fn(ht->table[j].key); 
     if((j > i && (loc <= i || loc > j)) || (j < i && (loc <= i && loc > j))) { 
      ht->table[i] = ht->table[j]; 
      ht->table[j].key = 0; 
      ht->table[j].value = 0; 
      close_gap(ht, j); 
      return; 
     } 
    } 
} 

Я только закодированным вокруг ошибок и предупреждений, я не проверял логику. Вы увидите, что я использовал malloc для выделения памяти для key и value. Очевидно, вам понадобится управление памятью на этих двух (то есть free()).

+0

Большое спасибо за исправление. Мне не приходило в голову, что компиляция не разделяет все символы, поэтому gcc не вызывал ошибку, когда я ссылался на hash_fn вместо ht-> hash_fn. Поэтому я не заметил этого, пока вы не указали его – user1299784

+0

@ user1299784: он дал ошибку компоновщика на gcc, который я использую на OS X. – cdarke

+0

Двойная проверка, без ошибок на Ubuntu 14.04. Я использовал «gcc -shared hash.c -o test.so -fPIC» - это правильные настройки для создания общих libs для ctypes? – user1299784

Смежные вопросы