C структура в Python - PullRequest
       15

C структура в Python

6 голосов
/ 24 февраля 2012

Существует libx.so, который экспортирует 2 функции, и struct,

typedef struct Tag {
    int num;
    char *name;
}Tag;

Tag *create(int n, char *n)
{
    Tag *t = malloc(sizeof(Tag));
    t->num = n;
    t->name = n;
    return t;
}

void use(Tag *t)
{
    printf("%d, %s\n", t->num, t->name);
}

Я хочу вызвать create в Python, а затем сохранить Tag *res, возвращаемый create, позже я позвоню use и передам Tag *res, сохраненный ранее, use, вот оно (просто продемонстрировать):

>>>libx = ctypes.CDLL("./libx.so")
>>>res = libx.create(c_int(1), c_char_p("a"))
>>>libx.use(res)

Приведенный выше код может быть неправильным, просто чтобы продемонстрировать, что я хочу сделать.

И моя проблема в том, как я могу сохранить результат, возвращаемый create? Поскольку он возвращает указатель на пользовательский struct, и я не хочу создавать аналог struct Tag в Python, c_void_p справится?

UPDATE

Из ответа @ Дэвида я до сих пор не совсем понял одну вещь:

указатель (c_char_p("a")) действителен только в течение позвоните на create. Как только create возвращает, этот указатель не дольше действителен.

И я присваиваю c_char_p("a") t->name в create, когда завершается вызов create, является ли t->name висящим указателем? Потому что, согласно приведенным словам, этот указатель больше не действителен после create. Почему c_char_p("a") больше не действителен?

Ответы [ 2 ]

4 голосов
/ 24 февраля 2012

Код C, который вы предоставляете, просто не будет работать. Вы должны быть более точными в том, какая сторона выделяет кучу памяти и отвечает за нее.

В вашем текущем примере вы передаете c_char_p("a") коду C. Однако указатель на эту память ctypes действителен только на время вызова create. Как только create вернется, указатель больше не действителен. Но вы взяли копию указателя внутри create. Таким образом, последующий вызов use может потерпеть неудачу.

Вам нужно будет взять копию содержимого этой строки и сохранить ее в структуре. Если вы сделаете это, вы можете безопасно использовать libx.create.restype = c_void_p.

Но если вы хотите, чтобы выделенная память была освобождена, вам нужно будет предоставить функцию destroy, соответствующую функции create. С этими изменениями код C будет выглядеть так:

Tag *create(int n, char *s)
{
    Tag *t = malloc(sizeof(Tag));
    t->num = n;
    t->name = strdup(s);
    return t;
}

void destroy(Tag *t)
{
    free(t->name);
    free(t);
}

Код Python будет выглядеть так:

libx = ctypes.CDLL("./libx.so")
libx.create.restype = c_void_p
res = libx.create(c_int(1), c_char_p("a"))
libx.use(res)
libx.destroy(res)
0 голосов
/ 24 февраля 2012

Python делает подсчет ссылок. Вам придется использовать Py_INCREF () и друзей для объектов, которые возвращаются из «внешних» библиотек.

ОБНОВЛЕНИЕ: я не знаю о загрузке .so с помощью python, возможно, метод, предложенный @David Hefferman, делает это автоматически.

ОБНОВЛЕНИЕ2: удалите меня!

...