Бесплатный 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, если да, то как мне это сделать?

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 это фиксированный размер, тогда вы можете просто объявить взятый как массив в структуре, забыть об использовании free, и использовать c_int * 9 скорее, чем POINTER(c_int) при объявлении типа taken в питоне.

Другие вопросы по тегам