使用 ctypes 在 python 中分配的 C 中写入缓冲区,并在 python 中再次读回

Write buffer in C which is allocated in python using ctypes, and read back again in python

问题陈述:

在 python 中使用 numpy 数组分配给定大小 16*100 的缓冲区,并在 C 中用一些结构内容填充该缓冲区,然后想在 python 中再次读回该缓冲区.

我有一个结构定义如下:

sample.c

    #include <stdio.h>

    typedef unsigned char           uint8_t;
    typedef short                   int16_t;
    typedef unsigned short          uint16_t;

    struct S{
        int a;
    };

    struct ins_data {
        int16_t offset1;
        int16_t num1;
        int16_t offset2;
        int16_t num2;
        void *buffer;  //==> Need to fill this buffer with struct S contents
    };

    struct desc_data {
        uint8_t id;
        void *ins_data;
    };

    void access_ins_data(struct ins_data* ptr) {
        int i = 0;

        struct S *p = (struct S*)ptr->buffer;
        printf("offset1: %d\n", ptr->offset1);
        ptr->offset1 = 12;
        ptr->num1 = 13;

        struct S tt;
        tt.a = 10;

        memcpy(p, &tt, sizeof(struct S));

        /* Want to fill this buffer in below fashion, but its not even working for single case.
         * |struct S s1|struct S s2| struct S s3|
         *
         */
        printf("S.a: %d\n", p->a);
    }

    void printLib() {
        printf("Oh Yeah.. you are here");
    }

    void foo(void *p) {
        struct desc_data *ptr = (struct desc_data*)p;
        printf("id: %d\n", ptr->id);
        access_ins_data(ptr->ins_data);
        struct ins_data *ss = (struct ins_data *)ptr->ins_data;
        struct S *pp = (struct S*)ss->buffer;
        printf("foo : %d\n", pp->a);
    }

用于生成.so文件的命令:gcc -o sample.so -shared -fPIC sample.c

下面是 python 世界的代码:

samplePython.py

    from ctypes import *
    import numpy as np

    class S(Structure):
        pass
    S._fields_ = [
        ('a', c_int32),
    ]

    class ins_data(Structure):
        pass
    int16_t = c_int16
    ins_data._fields_ = [
        ('offset1', int16_t),
        ('num1', int16_t),
        ('offset2', int16_t),
        ('num2', int16_t),
        ('buffer', c_void_p),
    ]

    class desc_data(Structure):
        pass
    uint8_t = c_uint8
    desc_data._fields_ = [
        ('id', uint8_t),
        ('ins_data', c_void_p),
    ]

    def get_ins_data():
        arr = np.zeros(16*100, dtype='uint8')
        enc_data = ins_data(-1,0,-1,0)
        enc_data.buffer = cast(np.ctypeslib.as_ctypes(arr), c_void_p)

        return enc_data

    from ctypes import cdll
    newA = cdll.LoadLibrary("sample.so")

    foo = newA.foo
    foo.argtypes = [c_void_p]
    foo.restype = None

    insData = get_ins_data()
    descData = desc_data(0, cast(byref(insData), c_void_p))
    foo(cast(byref(descData), c_void_p))

    print "descData.id", descData.id
    tt= cast(descData.ins_data, POINTER(ins_data))
    buff = cast(tt[0].buffer, POINTER(S))
    print "buffer content", buff.contents.a

输出:

id: 0
offset1: -1
S.a: 10
foo : 10
descData.id:  0
buffer content 1140653488 #This should come 10?

问题:

显示垃圾值的缓冲区内容。它应该显示 10.

在此先感谢,严重卡在这个简单的代码中。 :(

以下代码对我有用:

#include <stdio.h>
#include <stdint.h>
#include <string.h>

struct S {
    int32_t a;
};

struct ins_data {
    int16_t offset1;
    int16_t num1;
    int16_t offset2;
    int16_t num2;
    void *buffer;  //==> Need to fill this buffer with struct S contents
};

struct desc_data {
    uint8_t id;
    void *ins_data;
};

void access_ins_data(struct ins_data* ptr)
{
    struct S *p = (struct S*)ptr->buffer;
    printf("offset1: %d\n", ptr->offset1);
    ptr->offset1 = 12;
    ptr->num1 = 13;

    struct S tt;
    tt.a = 10;

    memcpy(p, &tt, sizeof(struct S));

    /* Want to fill this buffer in below fashion, but its not even working for single case.
        * |struct S s1|struct S s2| struct S s3|
        *
        */
    printf("S.a: %d\n", p->a);
}

void printLib()
{
    printf("Oh Yeah.. you are here");
}

void foo(void *p)
{
    struct desc_data *ptr = (struct desc_data*)p;
    printf("id: %d\n", ptr->id);
    access_ins_data(ptr->ins_data);
    struct ins_data *ss = (struct ins_data *)ptr->ins_data;
    struct S *pp = (struct S*)ss->buffer;
    printf("foo : %d\n", pp->a);
}

和 python 代码:

from ctypes import *
import numpy as np

class S(Structure):
    _fields_ = [
        ('a', c_int32)
    ]

class ins_data(Structure):
    _fields_ = [
        ('offset1', c_int16),
        ('num1', c_int16),
        ('offset2', c_int16),
        ('num2', c_int16),
        ('buffer', c_void_p)
    ]

class desc_data(Structure):
    _fields_ = [
        ('id', c_uint8),
        ('ins_data', c_void_p)
    ]

def get_ins_data():
    arr = create_string_buffer(16 * 100)
    #arr = np.zeros(16*100, dtype='uint8')
    insData = ins_data(-1, 0, -1, 0, 0)
    insData.buffer = cast(arr, c_void_p)
    #insData.buffer = cast(np.ctypeslib.as_ctypes(arr), c_void_p)
    return insData

from ctypes import cdll
newA = cdll.LoadLibrary("./sample.so")

foo = newA.foo
foo.argtypes = [c_void_p]
foo.restype = None

insData = get_ins_data()
descData = desc_data(0, cast(pointer(insData), c_void_p))
foo(byref(descData))

print("descData.id", descData.id)
tt = cast(descData.ins_data, POINTER(ins_data))
buff = cast(tt[0].buffer, POINTER(S))
print("buffer content", buff.contents.a)

当你需要创建一个真正的指针时,你不应该使用 byref 而应该使用 pointer

为了使用 Numpy,我们需要保留对局部变量(数组)的引用。例如这段代码没有段错误:

from ctypes import *
import numpy as np

class S(Structure):
    _fields_ = [
        ('a', c_int32)
    ]

class ins_data(Structure):
    _fields_ = [
        ('offset1', c_int16),
        ('num1', c_int16),
        ('offset2', c_int16),
        ('num2', c_int16),
        ('buffer', c_void_p)
    ]

class desc_data(Structure):
    _fields_ = [
        ('id', c_uint8),
        ('ins_data', c_void_p)
    ]


from ctypes import cdll
newA = cdll.LoadLibrary("./sample.so")

foo = newA.foo
foo.argtypes = [c_void_p]
foo.restype = None


arrNp = np.zeros(16*100, dtype='uint8')
arr = np.ctypeslib.as_ctypes(arrNp)

# If the following line is un-commented, the code segfault since python will GC the array referenced by `arr`
#arrNp = None

insData = ins_data(-1, 0, -1, 0, 0)
insData.buffer = cast(arr, c_void_p)

descData = desc_data(0, cast(pointer(insData), c_void_p))
foo(byref(descData))

print("descData.id", descData.id)
tt = cast(descData.ins_data, POINTER(ins_data))

print hex(descData.ins_data)
print hex(tt.contents.buffer)

buff = cast(tt[0].buffer, POINTER(S))
print("buffer content", buff.contents.a)

这里转换的C代码(main函数):

from ctypes import *
import numpy as np

class S(Structure):
    _fields_ = [
        ('a', c_int32)
    ]

class ins_data(Structure):
    _fields_ = [
        ('offset1', c_int16),
        ('num1', c_int16),
        ('offset2', c_int16),
        ('num2', c_int16),
        ('buffer', c_void_p)
    ]

class desc_data(Structure):
    _fields_ = [
        ('id', c_uint8),
        ('ins_data', c_void_p)
    ]


from ctypes import cdll
newA = cdll.LoadLibrary("./sample.so")

foo = newA.foo
foo.argtypes = [c_void_p]
foo.restype = None


A = create_string_buffer(16 * 100)
dData = desc_data()
iData = ins_data()

dData.id = 1

iData.offset1 = -1
iData.num1 = 0
iData.offset2 = -1
iData.num2 = 0
iData.buffer = cast(A, c_void_p)

dData.ins_data = cast(pointer(iData), c_void_p)
foo(byref(dData))

pp = cast(dData.ins_data, POINTER(ins_data))
p = cast(pp.contents.buffer, POINTER(S))

print("@@@@: {}".format(p[0].a))
print("@@@@: {}".format(p[1].a))
print("@@@@: {}".format(p[2].a))