【问题标题】:Write buffer in C which is allocated in python using ctypes, and read back again in python使用 ctypes 在 python 中分配的 C 中写入缓冲区,并在 python 中再次读回
【发布时间】:2018-09-01 10:29:37
【问题描述】:

问题陈述:

在 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。

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

【问题讨论】:

    标签: python c ctypes


    【解决方案1】:

    这里是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))
    

    【讨论】:

      【解决方案2】:

      以下代码对我有用:

      #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)
      

      【讨论】:

      • @AmitSharma 是的,我知道(抱歉)...我刚刚发现...我解决了 50% 的问题。我正在努力
      • int main() { uint8_t A[100*16] ={0}; struct desc_data dData; struct ins_data iData; dData.id = 1; iData.offset1 = -1; iData.num1 = 0; iData.offset2 = -1; iData.num2 = 0; iData.buffer = (void*)A; dData.ins_data = &amp;iData; foo(&amp;dData); struct ins_data *pp = (struct ins_data *)dData.ins_data; struct S *p = (struct S*)pp-&gt;buffer; printf("@@@@: %d\n", p-&gt;a); p++; printf("@@@@: %d\n", p-&gt;a); p++; printf("@@@@: %d\n", p-&gt;a); } 实际上我们需要将这段 C 代码转换为 ctypes python。
      • @AmitSharma 看起来必须保留对原始 numpy 数组的引用,否则 python 将垃圾收集它。而python会崩溃...
      • POINTER 是一个,用于从另一个类型生成一个类型。 POINTER(c_int16) 在 C 中:int16_t*。而pointer是一个函数获取ctype对象的地址
      猜你喜欢
      • 1970-01-01
      • 2019-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-21
      • 2017-08-04
      • 2021-12-11
      • 1970-01-01
      相关资源
      最近更新 更多