【问题标题】:Calling python function fetching 'c' like struct using cython and ctypes interface in c program在c程序中使用cython和ctypes接口调用python函数获取'c'之类的结构
【发布时间】:2020-06-27 11:19:14
【问题描述】:

我是 cython/ctypes 的新手,我正在尝试使用 cython 接口从 c 程序调用 python 函数,但数据为空或不正确。这是示例程序

python函数

$ cat c_struct.py
#!/usr/bin/env python2.7

from ctypes import *

class Request(Structure):
    _pack_ = 1
    _fields_ = [
            ('type', c_ubyte),
            ('subtype', c_ubyte),
            ('action', c_ubyte),
            ('checksum', c_ushort)
            ]

    def __repr__(self):
        return "'type': {}, 'subtype': {}, 'action': {}, 'checksum': {}".format(self.type,
                self.subtype, self.action, self.checksum)


req_msg = Request()
def get_message(typ):
    if typ == 1:
        req_msg.type = 12
        req_msg.subtype = 2
        req_msg.action = 3
        req_msg.checksum = 0x1234
        return req_msg
    else:
        return "String object From Python"

cython 包装器

$ cat caller.pyx
import sys
sys.path.insert(0, '')

from c_struct import get_message

cdef public const void* get_data_frm_python(int typ):
    data = get_message(typ)
    print "Printing in Cython -",data
    return <const void*>data

最后是我的“C”调用者

cat main.c
#include <Python.h>
#include "caller.h"
#include <stdio.h>

typedef struct {
    uint8_t type;
    uint8_t subtype;
    uint8_t action;
    uint16_t checksum;
} __attribute__ ((packed)) request_t;

int
main()
{
    PyImport_AppendInittab("caller", initcaller);
    Py_Initialize();
    PyImport_ImportModule("caller");

        const char* str = get_data_frm_python(2);
        printf("Printing in C - %s\n", str);

        const request_t* reqP = (request_t*)get_data_frm_python(1);
        printf("Printing in C - %u, %u, %u, %u\n", reqP->type, reqP->subtype, reqP->action, reqP->checksum);

    return 0;
}

还有一个简单的 makefile 来构建它

$ cat Makefile
target = main
cy_interface = caller

CY := cython
PYTHONINC := $(shell python-config --includes)
CFLAGS := -Wall $(PYTHONINC) -fPIC -O0 -ggdb3
LDFLAGS := $(shell python-config --ldflags)

CC=gcc

all: $(target)

%.c: %.pyx
        $(CY) $+

%.o: %.c
        $(CC) -fPIC $(CFLAGS) -c $+

$(target): $(cy_interface).o $(target).o
        $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

最后是输出:(

$ ./main
Printing in Cython - String object From Python
Printing in C -
Printing in Cython - 'type': 12, 'subtype': 2, 'action': 3, 'checksum': 4660
Printing in C - 1, 0, 0, 0

有人可以帮助我了解我做错了什么吗?

注意:- 如果我将 void* 更改为 char*,则至少可以正确获取字符串数据,但 struct 会出现段错误

$ cat caller.pyx
import sys
sys.path.insert(0, '')

from c_struct import get_message

cdef public const void* get_data_frm_python(int typ):
    data = get_message(typ)
    print "Printing in Cython -",data
    return <const char*>data


$ ./main
Printing in Cython - String object From Python
Printing in C - String object From Python
Printing in Cython - 'type': 12, 'subtype': 2, 'action': 3, 'checksum': 4660
TypeError: expected string or Unicode object, Request found
Exception TypeError: 'expected string or Unicode object, Request found' in 'caller.get_data_frm_python' ignored
Segmentation fault

【问题讨论】:

  • Request 不是结构本身,而是包装它的 python 对象。要获取 c 代码的结构对象的地址,请使用 ctypes.addressof
  • @ead - 你能举个例子吗?你是说return ctypes.addressof(req_msg) 吗?

标签: cython ctypes


【解决方案1】:
<const void*>data

data 是一个 Python 对象(PyObject*)。此行将 PyObject* 转换为无效。将PyObject* 解释为字符串或request* 绝对是不合理的。

<const char*>data

这将获得一个指向 Python 字符串的第一个元素的指针(您的结构不是)。指针仅在data 还活着时才有效。不幸的是,数据是一个函数局部变量,因此几乎可以立即释放。很难确切地说出这里发生了什么,但肯定是错误的。


我真的不明白你在这里想要达到什么目标,所以我很难就如何做得更好提供建议。也许放弃 ctypes 并将 Cython 与 C 联合使用?

【讨论】:

  • 感谢@DavidW 的回复,python 对象中的所有内容都不是吗?我没有太多的python内部结构,但是当我return req_msg我假设它返回一个引用/指针时?所以局部变量(data)最终会再次返回 ptr/ref。
  • 是的 - 基本上一切都是 Python 对象。关键是,一旦您将 Python 对象设置为 void*,您就失去了所有引用计数(因此它不太可能是安全的),并且您实际上可以用 void* Python 对象做任何有意义的事情.
  • 我意识到这是一个无益的答案,因为它只会告诉您您的代码不起作用。但是,我真的不明白您要做什么,而且我认为这可能很难安全地做到。
【解决方案2】:

无论如何,这对我来说是成功的,不是一个完美的,而是一些东西。

$ cat caller.pyx
import sys
sys.path.insert(0, '')

from libc.stdint cimport uintptr_t
from c_struct import get_message

cdef public const void* get_data_frm_python(int typ):
    data = get_message(typ)
    if isinstance(data, str):
        return <const char*>(data)
    cdef uintptr_t ptr = <uintptr_t>(get_message(typ))
    return <const void*>(ptr)
def get_message(typ):
    if typ == 1:
        req_msg.type = 12
        req_msg.subtype = 2
        req_msg.action = 3
        req_msg.checksum = 0x1234
        return addressof(req_msg)
    else:
        return "String object From Python"
$ ./main
Printing in C - String object From Python
Printing in C - 12, 2, 3, 4660

【讨论】:

    猜你喜欢
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多