【问题标题】:Python ctypes passing pointer to mutable pointerPython ctypes将指针传递给可变指针
【发布时间】:2021-11-02 15:31:54
【问题描述】:

我有以下 C 库

struct algo_hdl_str_t {
  size_t size;
  void* data;
};

typedef algo_hdl_str_t* algo_hdl_t;

/**
 * @brief Encode data
 *
 * @param type[in] type of data
 * @param data[in] pointer to data
 * @param size[in] size of data
 * @param hdl[out] handler to allocated encoded data
 *
 * @return status
 */
algo_status_t algo_encode(algo_type_t const type, void const* const data, size_t const size, algo_hdl_t* const hdl);

/**
 * @brief Free message memory
 *
 * @param hdl[in] handler to message
 */
void algo_free(algo_hdl_t const hdl);

为了更了解这个库在这里做什么是单元测试:

algo_hdl_t hdl_enc;
char *in_str = (char *)"some_weird_string";
CHECK(ALGO_SUCCESS == algo_encode(ALGO_GENERAL, in_str, strlen(in_str), &hdl_enc));
CHECK(0U == memcmp(algo_data(&hdl_enc), expected_data, algo_data_size(&hdl_enc)));
algo_free(hdl_enc)

我想对此内存进行 python 绑定,但我未能在 Python 绑定代码中将指针传递给处理程序

import ctypes, os
from enum import Enum

class MessType(Enum):
    UNKNOWN = 0
    EXAMPLE = 1

def encode(message_type, data):
    path = os.path.abspath('../../../build/libalgo.dylib')
    lib = ctypes.CDLL(path)

    lib.algo_encode.argtypes = ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_void_p
    lib.algo_encode.restype = ctypes.c_int

    handler_mem = ctypes.create_string_buffer(8)
    handler = ctypes.cast(handler_mem, ctypes.c_void_p)
  
    data_ptr = (ctypes.c_void_p).from_buffer_copy(data)

    result = lib.algo_encode(message_type.value, data_ptr, len(data), handler)

    print(result)


if __name__ == "__main__":
    data = b'some_weird_string' + b'\x00'
    encode(MessType.EXAMPLE, data)

如何传递 algo_encode 的最后一个参数以使其工作?目前我有“分段错误”错误,因为 - 我猜 - 我正在传递指向不可变数据的指针......我确实尝试了更多“传递这个处理程序”的方法,但每个方法都失败了。

handler = ctypes.c_void_p() # fails with C library assertion telling we are passing nullptr
handler = ctypes.c_void_p(1) # failes with segmentation fault

由于我是python新手,请给我一点指导:)谢谢

【问题讨论】:

  • 我发现了问题:这不起作用data_ptr = (ctypes.c_void_p).from_buffer_copy(data)线程可以标记为已解决:)
  • 您可以回答自己的问题并将其标记为答案,或者直接删除。
  • @MarkTolonen 当我结束这个工作时,我一定会发布解决方案以使其成为值得堆栈溢出的帖子

标签: ctypes


【解决方案1】:

对于这样的问题,制作一个简单的 C 代码工作示例有助于阐明其工作原理。这是我最好的猜测。它分配返回结构并用提供的数据和长度填充它:

test.c

#include <stdlib.h>
#include <string.h>

#define API __declspec(dllexport)

struct algo_hdl_str_t {
  size_t size;
  void* data;
};

typedef struct algo_hdl_str_t* algo_hdl_t;
typedef int algo_status_t;
typedef int algo_type_t;

API algo_status_t algo_encode(algo_type_t const type, void const* const data, size_t const size, algo_hdl_t* const hdl) {
    *hdl = malloc(sizeof(struct algo_hdl_str_t));
    (*hdl)->data = malloc(size);
    memcpy((*hdl)->data, data, size);
    (*hdl)->size = size;
    return 0;
}

API void algo_free(algo_hdl_t const hdl) {
    free(hdl->data);
    free(hdl);
}

这里是ctypes Python 代码来阅读它:

import ctypes as ct

# Matching structure definition
class algo_hdl_str_t(ct.Structure):
    _fields_ = (('size',ct.c_size_t),
                ('data',ct.c_void_p))

# Equivalent typedef
algo_hdl_t = ct.POINTER(algo_hdl_str_t)

dll = ct.CDLL('./test')
dll.algo_encode.argtypes = ct.c_int,ct.c_void_p,ct.c_size_t,ct.POINTER(algo_hdl_t)
dll.algo_encode.restype = ct.c_int
dll.algo_free.argtypes = algo_hdl_t,
dll.algo_free.restype = None

hdl = algo_hdl_t()
data = b'some byte data\0nulls ok'
dll.algo_encode(1,data,len(data),ct.byref(hdl))
print(hdl.contents.size)
# hdl.contents obtains the structure.
# Cast hdl.contents.data from void* to char* and slice the pointer to the
# desired length to view the raw data.
print(ct.cast(hdl.contents.data,ct.POINTER(ct.c_char))[:hdl.contents.size])
dll.algo_free(hdl)

输出:

23
b'some byte data\x00nulls ok'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-09
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 2017-01-22
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    相关资源
    最近更新 更多