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

时间:2020-05-25 19:43:14

标签: python c++ arrays string ctypes

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

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

并且从Python我想将args作为字符串列表进行传递,即:

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

我以前找到了this,所以我用过

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[]

2 个答案:

答案 0 :(得分:3)

为了提供具体示例,我将其用作.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确保为空指针哨兵留有空间。

答案 1 :(得分:2)

ctypes没有可用于扩展编写器的C / C ++公共公用API,因此cppyy对ctypes的处理必然有些笨拙。出问题的是,生成的const char*的ctypes数组的类型为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 ++版本替换原始的C API:

# 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

相关问题