Как вернуть массив из С++-функции в Python с использованием ctypes - программирование
Подтвердить что ты не робот

Как вернуть массив из С++-функции в Python с использованием ctypes

Я использую ctypes для реализации функции С++ в Python. Функция С++ должна возвращать указатель на массив. К сожалению, я не понял, как получить доступ к массиву в Python. Я попробовал numpy.frombuffer, но это не удалось. Он просто вернул массив произвольных чисел. Очевидно, я не использовал его правильно. Вот простой пример с массивом размером 10:

Содержимое функции .cpp:

extern "C" int* function(){
int* information = new int[10];
for(int k=0;k<10;k++){
    information[k] = k;
}
return information;
}

Содержимое wrapper.py:

import ctypes
import numpy as np

output = ctypes.CDLL('./library.so').function()

ArrayType = ctypes.c_double*10
array_pointer = ctypes.cast(output, ctypes.POINTER(ArrayType))
print np.frombuffer(array_pointer.contents)

Чтобы скомпилировать файл С++, я использую:

g++ -c -fPIC function.cpp -o function.o
g++ -shared -Wl,-soname,library.so -o library.so function.o

Есть ли у вас какие-либо предложения о том, что мне нужно сделать для доступа к значениям массива в Python?

4b9b3361

Ответ 1

function.cpp возвращает массив int, а wrapper.py пытается интерпретировать их как парные. Измените ArrayType на ctypes.c_int * 10, и он должен работать.


Скорее просто использовать np.ctypeslib вместо frombuffer самостоятельно. Это должно выглядеть примерно так:

import ctypes
from numpy.ctypeslib import ndpointer

lib = ctypes.CDLL('./library.so')
lib.function.restype = ndpointer(dtype=ctypes.c_int, shape=(10,))

res = lib.function()

Ответ 2

Ваш код python будет работать после некоторых незначительных изменений:

import ctypes

f = ctypes.CDLL('./library.so').function
f.restype = ctypes.POINTER(ctypes.c_int * 10)
print [i for i in f().contents] # output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

В основном есть два изменения:

  • удалите связанный с numpy код и вызов ctypes.cast, так как они нам не нужны.

  • укажите тип возврата в ctypes.POINTER(ctypes.c_int * 10).

    По умолчанию предполагается, что внешние функции возвращают тип C int, поэтому нам нужно изменить его на нужный тип указателя.

Кстати, возвращение массива new ed из кода C в код Python кажется неуместным. Кто и когда освободит память? Лучше создавать массивы в коде Python и передавать их на C-код. Таким образом, ясно, что код Python владеет массивами и берет на себя ответственность за создание и восстановление их пространств.