为了账号安全,请及时绑定邮箱和手机立即绑定

如何从内存分配发生在C层的Python脚本将float*数组传递给C方法

如何从内存分配发生在C层的Python脚本将float*数组传递给C方法

喵喔喔 2023-10-26 15:13:07
我试图从Python脚本调用C方法,C方法调用反过来C++方法。我使用 malloc() 在 getResults() 方法内分配数组。现在的问题是如何将参数传递给 python 脚本中的 float* oresults,其内存分配发生在 C 层内。这是io.cint 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;}TypeError: byref() 参数必须是 ctypes 实例,而不是 '_ctypes.PyCPointerType' 这是我运行脚本时收到的错误。我对如何在脚本中发送 float *oresults 的参数有点困惑。
查看完整描述

2 回答

?
繁星点点滴滴

TA贡献1803条经验 获得超3个赞

在 C++ 代码中,签名int getResults(..., float* oResults)无法将分配的指针传回调用者。线路

oResults = (float*)malloc(results.size() * sizeof(float));

在 getResults 中本地设置oResults指针,而不影响调用者。为了输出指针,您必须使用return它或使用指针到指针参数:int getResults(..., float** oResults)

在Python代码中,我不熟悉ctypes,但它看起来float_values = POINTER(c_float)是一个问题。为浮点指针POINTER(c_float)创建 Python类型。您想要POINTER(c_float)()创建这样一个指针的实例(最初为空)。

查看完整回答
反对 回复 2023-10-26
?
慕婉清6462132

TA贡献1804条经验 获得超2个赞

该float* oResults参数是按值传递的,因此不可能返回该参数中已分配的指针。相反,使用float** oResults.


另外,float_values = POINTER(c_float)是类型,而不是类型的实例。所以byref(float_values)相当于无效的 C &(float*)。相反,您需要一个指针的实例POINTER(c_float)()(注意括号)并通过引用传递它,类似于 C float *p; func(&p)。这会将指针按地址传递给 C 函数,然后函数可以将其修改为输出参数。


这是一个简化的示例,仅关注int *oRescount和float** oResults参数。还需要一个释放分配的函数:


测试.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]


查看完整回答
反对 回复 2023-10-26
  • 2 回答
  • 0 关注
  • 125 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信