没有副本就不能使用 PyString,但可以使用 ctypes。原来ctypes.c_char_p 基本上就像一个字符串。例如下面的 C 代码:
static char* names[7] = {"a", "b", "c", "d", "e", "f", "g"};
PyObject *pFunc, *pArgs, *pValue;
pFunc = td_py_get_callable("my_func");
pArgs = PyTuple_New(2);
pValue = PyLong_FromSize_t((size_t) names);
PyTuple_SetItem(pArgs, 0, pValue);
pValue = PyLong_FromLong(7);
PyTuple_SetItem(pArgs, 1, pValue);
pValue = PyObject_CallObject(pFunc, pArgs);
然后可以使用以下python my_func传递地址和字符串数量:
def my_func(names_addr, num_strs):
type_char_p = ctypes.POINTER(ctypes.c_char_p)
names = type_char_p.from_address(names_addr)
for idx in range(num_strs):
print(names[idx])
当然,谁真的想在 Python 中传递地址和长度。如果我们需要使用它们,我们可以将它们放在一个 numpy 数组中并传递然后强制转换:
def my_func(name_addr, num_strs):
type_char_p = ctypes.POINTER(ctypes.c_char_p)
names = type_char_p.from_address(names_addr)
// Cast to size_t pointers to be held by numpy
p = ctypes.cast(names, ctypes.POINTER(ctypes.c_size_t))
name_addrs = numpy.ctypeslib.as_array(p, shape=(num_strs,))
// pass to some numpy functions
my_numpy_fun(name_addrs)
挑战在于评估 numpy 数组的索引只会给你一个地址,但内存与原始 c 指针相同。我们可以转换回ctypes.POINTER(ctypes.c_char_p) 来访问值:
def my_numpy_func(name_addrs):
names = name_addrs.ctypes.data_as(ctypes.POINTER(ctypes.c_char_p))
for i in range(len(name_addrs)):
print names[i]
这并不完美,因为我不能使用 numpy.searchsorted 之类的东西在 numpy 级别进行二进制搜索,但它确实在没有足够副本的情况下绕过 char*。