#python #c #arrays #c #ctypes
#python #c #массивы #c #типы ctypes #ctypes
Вопрос:
Я пытаюсь вызвать методы C из скрипта Python, вызовы метода C включают метод C . Я выделяю массив внутри метода GetResults(), используя malloc(). Теперь проблема заключается в том, как передать аргументы float * oresults в скрипте python, выделение памяти которого происходит внутри уровня C. Это io.c
int getResults(char* iFilename, char* iStagename, int iStateidCnt,
int* Stateids, int iEntityIdCount, int* iEntityids, char* iEntityType,
char* iVariablegroup, char* ivariable, int *oRescount,
float* oResults)
{
int Status, i;
EString etype(iEntityType), stagename(iStagename);
EString vargroup(iVariablegroup);
std::vector<ERF_INT> entity_ids;
std::vector<ERF_INT> stateids;
std::vector<ERF_FLOAT> results;
_CopyIntArrayIntoVector(iStateidCnt, Stateids, stateids);
_CopyIntArrayIntoVector(iEntityIdCount, iEntityids, entity_ids);
CreateIoInstance(iFilename, iStagename);
ioData pIodata = CreateIoDataInstance();
if (iEntityIdCount <= 0)
pIodata.setWholeSection(true);
else
{
pIodata.setWholeSection(false);
pIodata.setEntityList(entity_ids);
}
pIodata.setStateList(stateids);
pIodata.setType(etype);
pIodata.setVariableGroup(iVariablegroup);
pIodata.setVariable(ivariable);
//This is C method
Status = pIo->get_results(pIodata, results);
*oRescount = results.size();
//allocation for oresults whose size > 2
oResults = (float*)malloc(results.size() * sizeof(float));
_CopyVectorIntoDoubleArray(results, oResults);
return Status;
}
test.py
from ctypes import *
import os, sys
dll = CDLL('D:\erf_utils_python\erf_utils_io.dll')
dll.getresults.argtypes = (c_char_p,c_char_p,c_int,POINTER(c_int),c_int,POINTER(c_int),c_char_p,
c_char_p,c_char_p,POINTER(c_int),POINTER(c_float))
dll.getresults.restype = c_int
def make_array(ctype,arr):
return len(arr),(ctype * len(arr))(*arr)
def getresults(filename,stagename,sids,eids,entitytype,groups,variables):
if(len(sids)>0):
stateidcount,stateids = make_array(c_int,sids)
if(len(eids)>0):
entityidcount,entityid = make_array(c_int,eids)
oresultlen = c_int()
float_values = POINTER(c_float)
err = dll.getresults(filename,stagename,stateidcount,stateids,entityidcount,entityid,
entitytype,groups,variables,byref(oresultlen), byref(float_values))
return err,oresultlen.value, float_values
filename = b'D:\inputfile.h5'
stagename = b"post"
stateids = [2]
stateidcount = 1
entityidcount = 1
entityid = [1]
entitytype = b"test"
variablecount = 1
variablegroup = b"testdata"
variable = b"next"
err,oreslen,ores = getresults(filename,stagename,stateids,entityid,entitytype,variablegroup,variable)
TypeError: аргумент byref() должен быть экземпляром ctypes, а не
‘_ctypes .PyCPointerType’ это ошибка, которую я получаю при запуске
скрипта. Я немного смущен тем, как отправить аргумент для float
* или результаты в скрипте.
Ответ №1:
В коде C подпись int getResults(..., float* oResults)
не может передать выделенный указатель обратно вызывающему. Строка
oResults = (float*)malloc(results.size() * sizeof(float));
устанавливает oResults
указатель локально в GetResults, не затрагивая вызывающего. Чтобы вывести указатель, вы должны либо return
его, либо использовать аргумент указателя на указатель : int getResults(..., float** oResults)
.
В коде Python я не знаком с ctypes, но, похоже float_values = POINTER(c_float)
, это проблема. POINTER(c_float)
создает тип Python для указателя на float. Вы хотели POINTER(c_float)()
бы создать экземпляр такого указателя (который изначально равен нулю).
ctypes документация по указателям: https://docs.python.org/3/library/ctypes.html#pointers
Ответ №2:
float* oResults
Параметр передается по значению, поэтому невозможно вернуть выделенный указатель в этом параметре. Вместо этого используйте a float** oResults
.
Кроме того, float_values = POINTER(c_float)
это тип, а не экземпляр типа. So byref(float_values)
эквивалентно недопустимому C amp;(float*)
. Вместо этого вам нужен экземпляр указателя POINTER(c_float)()
(обратите внимание на круглые скобки) и передайте его по ссылке, аналогично C float *p; func(amp;p)
. Это передаст указатель по адресу функции C, которая затем может изменить его в качестве выходного параметра.
Вот упрощенный пример, в котором основное внимание уделяется только параметрам int *oRescount
and float** oResults
. Также необходима функция для освобождения выделения:
test.cpp
#include <vector>
#define API __declspec(dllexport)
extern "C" {
API int getResults(size_t *oRescount, float** oResults) {
std::vector<float> results {1.25,2.5,3.75,5.0}; // Simulated results
*oRescount = results.size(); // Return size of results
auto tmp = new float[results.size()]; // allocate
for(size_t i = 0; i < results.size(); i) // copy vector to allocation
tmp[i] = results[i];
*oResults = tmp; // return allocation
return 0;
}
API void freeResults(float* oResults) {
delete [] oResults;
}
}
test.py
from ctypes import *
dll = CDLL('./test')
dll.getResults.argtypes = POINTER(c_size_t),POINTER(POINTER(c_float))
dll.getResults.restype = c_int
def getresults():
oRescount = c_size_t() # instance to hold the returned size
oResults = POINTER(c_float)() # instance of a float* to hold the returned allocation.
err = dll.getResults(byref(oRescount), byref(oResults))
# oResults is a float* and it is possible to index past the end.
# Make a copy into a Python list slicing to the correct size,
# then free it so there is no memory leak.
results = oResults[:oRescount.value]
dll.freeResults(oResults)
return err,results
err,ores = getresults()
print(err,ores)
Вывод:
0 [1.25, 2.5, 3.75, 5.0]
Комментарии:
1. Я выделил память, как вы показали здесь, и я получаю сообщение OSError: exception: чтение нарушения доступа 0x0000000000000000..in питон… когда я тестировал только метод C, строка *oResults = temp выдает исключение nullptr. есть ли какой-либо другой способ выделения памяти на C, например, с помощью malloc
2. Я попробовал вашу реализацию для getresults(), и она работает хорошо, но когда я использую свою реализацию, она выдает ошибку, я думаю, std::vector<float> результаты {1.25,2.5,3.75,5.0}; это статическое распределение, и оно работает, но в моей ситуации я получаю векторные результаты отдругой C method.is если это проблема, пожалуйста, помогите мне
3. @sand Реализация является общей, а вектор является локальной переменной. Результат копируется в память, выделенную для кучи. Генерируйте случайные числа случайного размера, и это все равно будет работать.