2016-07-29 6 views
2

Учитывая простой C файл:ctypes структура, которая возвращается из библиотеки

#include <stdio.h> 

typedef struct point { 
    int x; 
    int y; 
} POINT; 

POINT get_point() 
{ 
    POINT p = {1, 2}; 
    return p; 
} 

И у меня есть простой питон файл:

from ctypes import * 
import os 

lib_name = '/testlib.so' 
test_lib = CDLL(os.getcwd() + lib_name) 

class POINT(Structure): 
    _fields_ = [('x', c_int), 
       ('y', c_int)] 

# Sets p1 to the integer 1 
p1 = test_lib.get_point() 
# Sets p2 to the struct POINT with values {1, 0} 
p2 = POINT(test_lib.get_point()) 

Как я могу установить мое возвращенное значение для структуры POINT со значениями {1, 2} ?

+0

Любая причина вы используете тег для 'ЬурейеЕ 'ed' struct'? Довольно бесполезно. Если вы не понимаете этот комментарий, вам нужно узнать о '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''' '' '' '' '' '' '' '' '' '' '' '' '. – Olaf

+0

@Olaf Я всегда использовал 'typedef' для' struct 'в C для псевдонимов. Есть что-то по своей сути неправильно? –

+0

Я не комментировал использование 'typedef'. Но ваш комментарий подтверждает мое предположение правильным. Не просто следуйте шаблону, но узнайте **, почему ** вы должны/должны написать что-то конкретным образом! – Olaf

ответ

1

То, что вы спрашиваете, является единственной проблемой в вашем примере. Чтобы ответить только на то, что вы спросили вначале: вы должны аннотировать тип возврата функции C, так что ctypes знает, что это адрес памяти - в противном случае по умолчанию это (4 байта) целое число (в то время как в любой 64-байтной ОС указатели 8 байтов).

Затем вы можете создать Python стороны POINT структуру, используя (скрытый) метода «FROM_ADDRESS» в классе POINT:

test_lib.get_point.restype = c_void_p 
p = POINT.from_address(test_lib.get_point()) 

print(p.x, p.y) 

До этого работает, однако, у вас есть более фундаментальный вопрос о С сторона: Структура POINT, которую вы объявляете на вашем примере, существует только в то время, когда get_point работает и освобождается после этого. Приведенный выше код приведет к ошибке сегментации.

Ваш код C должен правильно распределять память. Кроме того, вы должны принять меры для освобождения структур данных, которые вы выделяете в C, - иначе у вас будет утечка памяти, поскольку каждый вызов функции в C выделяет больше памяти, и вы не освобождаете это. (Обратите внимание, что эта память не будет освобождена сама по себе, когда объект POINT Python выходит за рамки).

Ваш код C может выглядеть так:

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

typedef struct point { 
    int x; 
    int y; 
} POINT; 

POINT *get_point() 
{ 
    POINT *p; 
    POINT initial = {1, 2}; 
    p = malloc(sizeof(POINT)); 
    *p = initial; 
    return p; 
} 

void free_point(POINT *p) 
{ 
    free(p); 
} 

И с этой Python части:

from ctypes import * 
import os 

lib_name = '/testlib.so' 
test_lib = CDLL(os.getcwd() + lib_name) 

class POINT(Structure): 
    _fields_ = [('x', c_int), 
       ('y', c_int)] 

test_lib.get_point.restype = c_void_p 

p1 = POINT.from_address(test_lib.get_point()) 
print (p1.x, p1.y) 

test_lib.free_point(byref(p1)) 
del p1 

все должно работать.

(а именно так, что этот ответ полный пример ctypes, я добавить GCC команды для создания файла testlib:

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

)

+0

Спасибо за ответ! Как упоминалось в J.J.Hakala в моем первоначальном сообщении, выполнение 'test_lib.get_point.restype = POINT' также является действительным, если я хочу вернуть значение' struct' по значению. Это отличная ссылка для возврата указателей на структуру. –

+0

Я не уверен, что возвращение структуры «by_value» может работать вообще - помимо синтаксиса, конечно. Он должен существовать где-то в памяти. – jsbueno

+0

Структура может быть типом возврата в C, хотя я думаю, что его обычно избегают, как вопрос стиля. http://stackoverflow.com/questions/9653072/return-a-struct-from-a-function-in-c –