如何从 python 调用带有 Char** 参数和 int* 参数的 C 方法?
Posted
技术标签:
【中文标题】如何从 python 调用带有 Char** 参数和 int* 参数的 C 方法?【英文标题】:how to call C methods with Char** argument and int* arguments from python? 【发布时间】:2020-09-09 09:54:54 【问题描述】:这是加载 erf_utils_io_D.dll
的 module.py script
,其中包含 io.c
和 io.h
文件
我成功加载了库并传递了 ctype 参数,例如
c_int, c_float, POINTER(c_int), POINTER(c_Float)
module.py
//python code module.py
import sys
from ctypes import *
#load the required library and make sure the folder where erf_utils_io_D.dll is present
dll = CDLL('D:\\erf_utils_python\\erf_utils_io.dll')
getContourResults = dll.getContourResults
class Utility(object):
def __init__(self):
print('i am inside init')
self.stagename = "post"
self.stateids = (c_int * 1) (2934)
self.stateidcount = 1
self.entityidcount = 1
self.entityid = (c_int * 1) (1)
self.entitytype = "FPM"
self.variablecount = 1
self.ores = [1.0]
self.filename ='allinone_RESULT.erfh5'
#This is how char** is treted in python for variablegroup
self.variablegroup = ["FPM_Mach_Number"]
self.string_length = len(self.variablegroup)
self.select_type = (c_wchar_p * self.string_length)
self.select = self.select_type()
for key, item in enumerate(self.variablegroup):
self.select[key] = item
#This is how char** is treated infor variable
self.variable = ["FPM_Mach_Number"]
self.var_len = len(self.variable)
self.var_type = (c_wchar_p * self.var_len)
self.variable_list = self.var_type()
for key, item in enumerate(self.variable):
self.variable_list[key] = item
def run(self):
getContourResults.argtypes = (POINTER(c_char_p), POINTER(c_char_p), c_int, POINTER(c_int),
c_int, POINTER(c_int), POINTER(c_char), c_int, self.select_type ,
self.var_type, POINTER(c_float))
getContourResults.restype = (c_int)
err = getContourResults(self.filename, self.stagename, self.stateidcount,
self.stateids, self.entityidcount,self.entityid, self.entitytype, self.variablecount, self.select, self.variable_list, self.ores)
reader = Utility()
reader.run()
code.cpp 看起来像这样
extern "C"
#endif
__declspec(dllexport) int getContourResults(char* iFilename, char* iStagename, int iStateidCnt, int* Stateids,
int iEntityIdCount, int* iEntityids, char* iEntityType,
int iVariablecnt, char** iVariablegroup, char** ivariable,
float* oResults);
请告诉我如何将参数从 python 脚本传递给方法 getContourResults()
存在于 io.c
【问题讨论】:
通常,char**
应指向以 NUL 结尾的 char*
s 数组的开头;你确定这里是否真的是这样吗?
顺便说一句,这将很难测试答案,因为不是你的人不会有erf_utils_io.dll
。在标准库实用程序之间找到可比较的调用约定并围绕它构建minimal reproducible example 通常是获得更好结果的途径,因此回答您的问题的人将能够实际测试他们的答案(以及对这些答案进行判断和投票的人,同样)。
Python 不能直接调用这样的函数。您将需要创建一个包装函数,该函数公开与Python's C API 一致的接口以适应您的目标函数。您可以在 C 中直接将这样的包装器写入 C API,或者您也应该能够在 Cython 中编写它,这更像 Python。
前两个参数是getContourResults
是c_char_p,c_char_p
不是POINTER(c_char_p),POINTER(c_char_p)
。
【参考方案1】:
没有迹象表明该功能的实现,所以我做了一些假设。通过此示例,您应该能够适应您的实际用例。主要修复是 .argtypes
更正和使用 c_char_p
而不是 c_wchar_p
和一个简单的帮助函数将列表转换为 ctypes 数组。
以下是关于类型的一些说明:
c_char_p
== char*
在 C 中。传递一个字节字符串,例如b'string'
。
c_wchar_p
== wchar_t*
在 C 中。传递一个 Unicode 字符串,例如'string'
。
POINTER(c_char_p)
== char**
在 C 中。
POINTER(c_float)
== float*
在 C 中。创建存储 (c_float()
) 并传递 byref
。使用.value
成员将返回的浮点数作为 Python 整数访问。
test.cpp
#include <stdio.h>
extern "C" __declspec(dllexport) int getContourResults(char* iFilename, char* iStagename, int iStateidCnt, int* Stateids,
int iEntityIdCount, int* iEntityids, char* iEntityType,
int iVariablecnt, char** iVariablegroup, char** ivariable,
float* oResults)
printf("iFilename = %s\n"
"iStagename = %s\n"
,iFilename,iStagename);
for(int i = 0; i < iStateidCnt; ++i)
printf("Stateids[%d] = %d\n",i,Stateids[i]);
for(int i = 0; i < iEntityIdCount; ++i)
printf("iEntityids[%d] = %d\n",i,iEntityids[i]);
printf("iEntityType = %s\n",iEntityType);
for(int i = 0; i < iVariablecnt; ++i)
printf("iVariablegroup[%d] = %s\n",i,iVariablegroup[i]);
printf("ivariable[%d] = %s\n",i,ivariable[i]);
*oResults = 1.2f;
return 5;
test.py
from ctypes import *
dll = CDLL('./test')
dll.getContourResults = dll.getContourResults
dll.getContourResults.argtypes = (c_char_p,c_char_p,c_int,POINTER(c_int),c_int,POINTER(c_int),c_char_p,
c_int,POINTER(c_char_p),POINTER(c_char_p),POINTER(c_float))
dll.getContourResults.restype = c_int
def make_array(ctype,arr):
return len(arr),(ctype * len(arr))(*arr)
def getContourResults(filename,stagename,sids,eids,entitytype,groups,variables):
stateidcount,stateids = make_array(c_int,sids)
entityidcount,entityid = make_array(c_int,eids)
groups = [b'group1',b'group2']
variables = [b'var1',b'var2']
if len(groups) != len(variables):
raise ValueError('assuming groups and variables same length')
_,variablegroup = make_array(c_char_p,groups)
variablecount,variable = make_array(c_char_p,variables)
ores = c_float()
err = dll.getContourResults(filename,stagename,stateidcount,stateids,entityidcount,entityid,
entitytype,variablecount,variablegroup,variable,byref(ores))
return err,ores.value
sids = [1,2,3]
eids = [4,5,6]
groups = [b'group1',b'group2']
variables = [b'var1',b'var2']
err,ores = getContourResults(b'filename',b'stagename',sids,eids,b'entitytype',groups,variables)
print(f'err = err')
print(f'ores = ores:.2f')
输出:
iFilename = filename
iStagename = stagename
Stateids[0] = 1
Stateids[1] = 2
Stateids[2] = 3
iEntityids[0] = 4
iEntityids[1] = 5
iEntityids[2] = 6
iEntityType = entitytype
iVariablegroup[0] = group1
ivariable[0] = var1
iVariablegroup[1] = group2
ivariable[1] = var2
err = 5
ores = 1.20
【讨论】:
如果我们需要从一个方法中输出 char **值,我们如何在脚本中声明 argtypes 以及我们如何返回 @sandhya 使用minimal reproducible example 提出新问题。如果这个答案回答了这个问题,请考虑支持/接受它。见What to do when someone answers my question。int GetNames(char* iFilename, char** oNames) int oNumNames, oStatus; /*io* pIo = GetIoInstance();*/ std::vector<EString> names; CreateIoInstance(iFilename); oStatus = pIo->get_names(names); oNumNames = (int)names.size(); for (int ii = 0; ii < oNumNames; ii++) strcpy(oNames[ii], names[ii].c_str()); return 0;
这是C层的代码我需要从python层调用这个方法,我担心第二个参数如何为onames分配内存
@sandhya I answered 在您的其他问题中。
在上面的示例中,它输出 err 和 ores.value 并在输出中打印 1.20,所以我的问题是如果 *oresults = 1.20, 1.30, 1.50 那么我们如何为此分配内存float 数组以及如何在输出上打印它。(而且我们不知道 python 中矿石的大小来分配内存)以上是关于如何从 python 调用带有 Char** 参数和 int* 参数的 C 方法?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 django 模板中调用带有参数的 python 函数? [复制]
将 char 数组发送到 DLL 调用“不知道如何转换参数”时出错