У меня есть общая библиотека и два заголовочных файла. Я смог использовать библиотеку, создав привязки, используя swig
. Однако версия swig
довольно медленная, и мне не удалось включить поддержку массивов numpy
. Сейчас я пытаюсь вызвать функции библиотеки из Python, используя ctypes
.
Первый заголовок содержит блок extern "C"
, который экспортирует 3 функции через макросы (которые мне недоступны). Упрощенная версия заголовка выглядит так:
...
class Foo;
typedef Foo* FOO_HANDLE;
#if !defined(__cplusplus)
# error "C++ Compiler only"
#endif
extern "C"
{
APIPROTOTYPE_EXT( MakeAlgo, FOO_HANDLE, (void) );
APIPROTOTYPE_EXT( DestroyAlgo, void, ( FOO_HANDLE ) );
APIPROTOTYPE_EXT( AlgoProcess, void, ( FOO_HANDLE, StructOne *, StructTwo *) );
}
Обычно первая функция MakeAlgo
возвращает указатель на экземпляр класса Foo
, вторая функция DestroyAlgo
уничтожает экземпляр, а третья функция AlgoProcess
принимает в качестве входных данных две структуры и изменяет некоторые их значения на месте.
Второй заголовок содержит определения StructOne
и StructTwo
и некоторые константы. StructTwo
содержит несколько вложенных структур.
В Python я переписал все структуры, содержащиеся во втором заголовке, используя ctypes.Structure
. Я не публикую здесь весь код, но вот пример того, как я определил вложенную структуру:
class StructTwo(Structure):
_fields_ = [("foo", StructFoo),
("dummy8", c_short)]
class StructFoo(Structure):
_fields_ = [("bar", c_uint),
("reserv1", c_uint),
("reserv2", c_uint)]
Тогда мой код Python выглядит следующим образом (при условии, что структуры находятся в файле structures.py
):
from ctypes import *
from structures import StructOne, StructTwo
libfoo = ct.cdll.LoadLibrary(path/to/so/library)
makeAlgo = libfoo.MakeAlgo
makeAlgo.restype = c_void_p
makeAlgo.argtypes = []
destroyAlgo = libfoo.DestroyAlgo
destroyAlgo.restype = None
destroyAlgo.argtypes = [c_void_p]
submit = libfoo.AlgoProcess
submit.restype = None
submit.argtypes = [c_void_p, POINTER(StructOne), POINTER(StructTwo)]
handle = makeAlgo()
one = bar.StructOne()
two = bar.StructTwo()
submit(handle, byref(one), byref(two))
print(two.foo.bar) # unsigned int, should output 1, got 196611000 instead
destroyAlgo(handle)
После создания указателя на класс Foo
и отправки входных данных я проверяю некоторые значения в одной из структур, и они не соответствуют ожидаемому результату. Например, я знаю, что одно из полей установлено библиотекой только на 0 или 1, но я получаю странные результаты, например, 196611000.
Кто-нибудь знает, что не так (может, у кого-то была похожая проблема)? Может быть, так, как я определил структуры? Или, может быть, это указатель на класс C ++, который обрабатывается неправильно?
EDIT
Мне удалось решить начальную проблему. Похоже, что способ, которым я определяю структуры, был неправильным. Вместо приведенного выше кода, вложенные структуры должны передаваться по ссылке:
class StructTwo(Structure):
_fields_ = [("foo", POINTER(StructFoo)),
("dummy8", c_short)]
class StructFoo(Structure):
_fields_ = [("bar", c_uint),
("reserv1", c_uint),
("reserv2", c_uint)]
# Then to initialize the nested structure
foo = StructFoo()
two = StructTwo(pointer(foo)) # has to be pointer() not byref()
Однако теперь, чтобы получить доступ к полям StructFoo
, мне нужно сделать:
print(two.foo.contents.bar)
В реальном коде у меня может быть до 4 вложенных уровней. Есть ли более элегантный способ доступа к их полям, чем:
two.foo.contents.baz.contents.qux.contents.field_value