2014-12-29 4 views
2

Я имею эту структуру:Free calloc в общей библиотеки, используя ctypes

struct result { 
    int position; 
    int *taken; 
}; 

struct result get_result(int pos, int take[]){ 
    struct result res; 
    res.position = pos; 
    res.taken = take; 
    return res; 
} 

который я называю в функции:

struct result get_taken_values(){ 
    //some code 
    int position = 0; 
    int* chosen = calloc(9, sizeof(int));  
    //some more code 
    struct result res; 
    res.position = position; 
    res.taken = chosen; 
    return res; 
} 

Если бы я должен был использовать это в основном, я бы просто позвонить free(res.taken) in the end ,

Но я делаю это в разделяемую библиотеку с помощью:

gcc -fPIC -c get_taken_values.c 
ld -shared -soname libtest.so.1 -o my_library.so -lc get_taken_values.o 

Я буду использовать эту библиотеку в Python с использованием ctypes.

Нужно ли мне освобождать calloc, если да, то как это сделать?

+3

Поскольку массив является фиксированным размером, почему бы просто не определить 'int take [9]' в структуре? Тогда вам не нужны 'calloc' и' free'. ctypes обрабатывает подклассы 'ctypes.Structure'', установленные в 'restype', но вы также можете выделить структуру в Python и передать ее ссылкой для функции для изменения. – eryksun

+3

вы можете определить функцию 'free_taken_values ​​()', которая выполняет необходимую очистку. – jfs

ответ

1

Предполагая, что вы нуждаетесь в массив переменного размера для taken, то вы можете создать ctypes.Structure, дать ему метод __del__, который будет вызывать free. Вы можете позвонить по телефону free, загрузив libc. __del__ вызывается после последней ссылки на объект, выпавший из области видимости. Использование __del__ может вызвать проблемы, если ваши объекты содержат циклические ссылки, поскольку python не будет знать, какие объекты вызывать на первом этапе __del__ (так это не так, он просто держит объекты, висящие в памяти).

from ctypes import Structure, c_int, POINTER, CDLL 
from ctypes.util import find_library 

__all__ = ["get_taken_values", "Result"] 

libc = CDLL(find_library("c")) 
libmy_library = CDLL("./my_library.so") 

class Result(Structure): 
    _fields_ = [ 
     ("position", c_int), 
     ("taken", POINTER(c_int)), 
     ("size", c_int) 
    ] 

    def __str__(self): 
     return "result(position={}, taken={})".format(
      self.position, self.taken[:self.size]) 

    def __del__(self): 
     libc.free(self.taken) 

get_taken_values = libmy_library.get_taken_values 
get_taken_values.argtypes =() 
get_taken_values.restype = Result 

if __name__ == "__main__": 
    result = get_taken_values() 
    print("position is {} and value at position is {}".format(
     result.position, result.taken[result.position])) 
    print(result) 

Это делает предположение о том, что экземпляр питона Result является владельцем памяти. Кроме того, если taken действительно имеет переменный размер, тогда вам нужно будет указать член размера в своей структуре, который ссылается на Result. Если taken является фиксированным размером, вы можете просто объявить как массив в структуре, забудьте об использовании свободного и используйте c_int * 9, а не POINTER(c_int) при объявлении типа taken в python.

+0

@eryksun У меня только есть опыт работы с glibc и linux. Возможно, было бы лучше добавить изменения, относящиеся к среде выполнения Microsoft, для ответа самостоятельно. – Dunes

+0

@eryksun А теперь я вижу. – Dunes

+0

Пробовал ваш подход, он работает «любопытно», в C это дает мне следующее: «позиция: 69, взято [0]: 3, принято [1]: 9', в то время как python дает мне результат (позиция = 69, move = [5, -1]). POINTER (c_int) превращается в c_long, вместо этого он должен быть массивом int. –