0

你如何包装一个需要预分配char**作为参数来存储结果的 C 函数调用?我正在尝试返回 python 列表结果。

我找到了相反的例子,还有这个 ctypes 例子,但我不完全确定 ctypes 是 cython 中的合适方法。

作为参考,我正在练习包装openni库:
http ://openni.org/Documentation/Reference/classxn_1_1_pose_detection_capability.html

我包装的原始 C 签名是(它实际上是一个 C++ 方法,只是在内部包装了一个 C 函数):

/**
 * @brief Gets the names of all poses supported by this capability.

 * @param [out]     pstrPoses   Pre-allocated memory for the names of the supported poses.
 * @param [in,out]  nPoses      In input - size of the preallocated memory, in output
 *                              - the number of pose names.
 */

XnStatus GetAvailablePoses(XnChar** pstrPoses, XnUInt32& nPoses) const

XnChar只是一个 typedef char

到目前为止,这是我的尝试,它崩溃了:

from libc.stdlib cimport malloc, free

def get_available_poses(self):
    cdef: 
        int i 
        bytes name 
        XnStatus stat
        XnUInt32 size = self.handle.GetNumberOfPoses()
        XnChar **buf = <XnChar**>malloc(size * sizeof(XnChar*))

    if not buf:
        raise MemoryError()

    try:
        # this crashes: Segmentation fault
        stat = self.handle.GetAvailablePoses(buf, size)

        # if I could get to here, I would want to 
        # build a list to return (not saying this is
            # even correct either)
        for i in range(size):
            name = <char*>(buf[i])
            ...

    finally:
        free(buf)

该版本的 C 函数在技术上已被弃用,但较新的版本在我看来更可怕:

/**
 * Gets the names of all poses supported by this capability.

 * @param [out]     pstrPoses       Pre-allocated memory for the names of the supported poses.
 * @param [in]      nNameLength     Memory size for each pose name.
 * @param [in,out]  nPoses          In input - size of the preallocated memory, in output
 *                                  - the number of pose names.
 */

 XnStatus GetAllAvailablePoses(XnChar** pstrPoses, XnUInt32 nNameLength, 
                                XnUInt32& nPoses) const;

理想情况下,如果我能弄清楚如何传入一个正确的char**并生成一个列表,我会使用新的,它还需要我指定分配的名称的长度。

更新

我将这个问题简化为基本问题,以确保我一开始就做对了:

源代码:

//chars.h

void setChars(char** str_array, int size);

//chars.cc

#include "chars.h"

void setChars(char** str_array, int size) {
    for (int i = 0; i < size; i++) {
        char *s = "FOO";
        str_array[i] = s;
    }
}

赛通:

#chars.pxd

cdef extern from "chars.h":
    void setChars(char**, int)

#chars.pyx

from libc.stdlib cimport malloc, free

def py_setChars():
    cdef: 
        bytes s
        int i
        int size = 6
        char** buf = <char**>malloc(size * sizeof(char*))

    if not buf:
        raise MemoryError()

    out = []

    try:
        setChars(buf, size)

        for i in range(size):
            s = buf[i]
            out.append(s)

    finally:
        free(buf)

    return out

它按预期工作:

In [1]: import chars
In [2]: chars.py_setChars()
Out[2]: ['FOO', 'FOO', 'FOO', 'FOO', 'FOO', 'FOO']

我猜想GetAllAvailablePoses()我想使用的调用是期待某种我做得不对的预分配内存,因此参数要求每个字符的大小。

4

1 回答 1

0

玩够了我终于弄明白了。GetAllAvailablePoses确实希望您预先分配每个空间char*并告诉它要填充的空间有多大:

# foo.pxd

XnStatus GetAllAvailablePoses(XnChar **, XnUInt32, XnUInt32&)

# foo.pyx

def get_available_poses(self):
    cdef: 
        int i 
        bytes name 
        XnStatus stat
        XnUInt32 nameLength = 256
        XnUInt32 size = self.handle.GetNumberOfPoses()

    cdef XnChar **buf = <XnChar**>malloc(size * sizeof(XnChar*))

    if not buf:
        raise MemoryError()

    for i in range(size):
        buf[i] = <XnChar*>malloc(nameLength)

    out = [None]*size

    try:
        stat = self.handle.GetAllAvailablePoses(buf, nameLength, size)

        for i in range(size):
            name = <char*>buf[i]
            out[i] = name

    finally:
        free(buf)

    return out

# Out: ['Psi', 'CrossHandsPose', 'Wave', 'Click', 'RaiseHand', 'MovingHand']
于 2012-08-06T17:43:54.200 回答