CPPYY/CTYPES 将字符串数组作为 char* args[] 传递

CPPYY/CTYPES passing array of strings as char* args[]

本文关键字:char args 传递 CTYPES 字符串 数组 CPPYY      更新时间:2023-10-16

我最近才开始使用cppyyctypes,所以这可能是一个有点愚蠢的问题。我有以下C++功能:

float method(const char* args[]) {
...
}

从 Python 中,我想将args作为字符串列表传递,即:

args = *magic*
x = cppyy.gbl.method(args)

我以前发现过这个,所以我用

def setParameters(strParamList):
numParams    = len(strParamList)
strArrayType = ct.c_char_p * numParams
strArray     = strArrayType()
for i, param in enumerate(strParamList):
strArray[i] = param
lib.SetParams(numParams, strArray)

和来自 Python :

args = setParameters([b'hello', b'world'])

c_types.c_char_p需要一个字节数组。但是,当打电话x = cppyy.gbl.method(args)我得到

TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)

我不完全确定为什么这是错误的,因为args是一个<__main__.c_char_p_Array_2>的对象,我认为应该将其转换为const char* args[]

ctypes 没有可从 C/C++ 中用于扩展编写器的公共 API,因此 cppyy 对 ctypes 的处理必然有些笨拙。出错的是,生成的 ctypes 数组const char*const char*[2]类型而不是const char*[],并且由于 cppyy 对 ctypes 类型进行直接类型匹配,因此会失败。

按原样,某处的某些代码需要将 Python 字符串转换为低级 C 字符串,并在调用期间保留该内存。就我个人而言,我会使用一点C++包装器,而不必在 Python 方面考虑问题。关键是std::vector<std::string>可以处理必要的转换(例如,不需要bytes类型,但如果您愿意,当然可以允许(,并且可以保存临时内存。

因此,如果您得到这样的第三方界面(仅出于示例而将其内联为 cppyy(:

import cppyy
cppyy.cppdef("""
float method(const char* args[], int len) {
for (int i = 0; i < len; ++i)
std::cerr << args[i] << " ";
std::cerr << std::endl;
return 42.f;
}
""")

然后我会生成一个包装器:

# write a C++ wrapper to hide C code
cppyy.cppdef("""
namespace MyCppAPI {
float method(const std::vector<std::string>& args) {
std::vector<const char*> v;
v.reserve(args.size());
for (auto& s : args) v.push_back(s.c_str());
return ::method(v.data(), v.size());
}
}
""")

然后将原始 C API 替换为C++版本:

# replace C version with C++ one for all Python users
cppyy.gbl.method = cppyy.gbl.MyCppAPI.method

对于下游的任何其他人来说,事情都会像预期的那样:

# now use it as expected
cppyy.gbl.method(["aap", "noot", "mies"])

综上所述,显然cppyy没有理由不能自动进行这种包装。我创建了这个问题:https://bitbucket.org/wlav/cppyy/issues/235/automatically-convert-python-tuple-of

为了有一个具体的例子,我将使用它作为.cpp文件:

#include <cstdlib>
extern "C"
float method(const char* args[]) {
float sum = 0.0f;
const char **p = args;
while(*p) {
sum += std::atof(*p++);
}
return sum;
}

我假设它是用g++ method.cpp -fPIC -shared -o method.so编译的.鉴于这些假设,下面是一个如何从Python使用它的示例:

#!/usr/bin/env python3
from ctypes import *
lib = CDLL("./method.so")
lib.method.restype = c_float
lib.method.argtypes = (POINTER(c_char_p),)
def method(args):
return lib.method((c_char_p * (len(args) + 1))(*args))
print(method([b'1.23', b'45.6']))

我们创建一个 C 数组来保存 Python 参数。len(args) + 1确保有空指针哨兵的空间。