Python 可以组合swig生成的数据类型和ctypes函数

Python 可以组合swig生成的数据类型和ctypes函数,python,c,ctypes,swig,Python,C,Ctypes,Swig,我正在为C库编写一个带有ctypes的python包装器,我有一个返回结构的C函数。但该结构是在另一个C文件中定义的,该文件由swig包装 我简化了结构和代码 该结构由swig包裹 结构点{ int x; int-y; }; 此函数用ctypes包装 结构点添加点(结构点a、结构点b){ 结构点c; c、 x=a.x+b.x; c、 y=a.y+b.y; 返回c; } Python包装器 import swigModule#包含从c结构点生成的类点 导入ctypes _libc=ctypes

我正在为C库编写一个带有ctypes的python包装器,我有一个返回结构的C函数。但该结构是在另一个C文件中定义的,该文件由swig包装

我简化了结构和代码

该结构由swig包裹

结构点{
int x;
int-y;
};
此函数用ctypes包装

结构点添加点(结构点a、结构点b){ 结构点c; c、 x=a.x+b.x; c、 y=a.y+b.y; 返回c; } Python包装器

import swigModule#包含从c结构点生成的类点
导入ctypes
_libc=ctypes.CDLL('./c_file.so')
def添加点(a、b):
add_points=_libc.add_points
add_points.argtypes=[swigModule.point,swigModule.point,]
添加_points.restype=swigModule.point,
结果=添加点(a、b)
返回结果
问题是,我不能将swig生成的类
point
用作ctypes中的restype和argtype。但我不能像这样编写自己的结构包装器

类点(ctypes.Structure):
_字段=[(“x”,ctypes.c_int),
(“y”,ctypes.c_int)]
因为C结构的字段是隐藏的,所以我无法访问源代码。我只知道swig包装器中的结构名称

我有两个C文件,一个是用swig生成的,具有结构数据类型。另一个有函数,用ctypes包装。我想在ctypes函数中使用swig中的数据类


如何将从c结构生成的swig类映射到ctypes类,以便将其用作返回类型?

这确实是一个有趣的问题!以前有人问过,请参阅。 然而,这个问题并没有一个公认的答案。在互联网上做研究几乎找不到有用的信息

普遍的共识似乎是“使用
ctypes
SWIG”。 因此,恐怕您的问题的答案是否定的:-(


我从研究这个问题中得到的信息:

  • 当您必须与多种语言交互时,SWIG会更好
  • ctypes
    在您不想编译东西时更好
  • <>代码> cType 与C代码很好,但是只有C++代码有<代码>外部c<代码>接口。 >代码> BooSt.python 为C++与Python的接口提供了很多。

您可能会找到信息丰富的答案。

完全有可能让SWIG和CType以各种不同的方式协同工作,我将在下面的几个例子中展示。不过,有一个很大的警告:根据我的经验,它几乎永远不会为您提供可维护、可移植、可扩展等功能。因此,我倾向于如果可能的话,ed会倾向于其他选择(这些可能包括使用任何合适的说服手段向原始作者索要资料来源,从头开始重新编写整个内容,或者简单地使用其他库)

无论如何,让我们假设您已经为给定的Python版本构建了一个SWIG包装库。无论出于何种原因,它们都没有包装您最喜欢的函数。因此,您只需要使用ctypes将某些东西修补到SWIG生成的代码中。如果:

  • 要调用的函数仅通过指针而不是值获取和返回对象
  • 您想要调用的函数只返回您的类型的现有实例,而不malloc新的实例(这有一个解决方案,但它又开始让事情变得困难,非常快)
  • <> LI>你关心的结构实际上是包裹了所有你关心的成员,如果是C++代码,它们是POD类型。

    你的情况并不完全符合这些限制,但是为了进行一个预热练习,让我们来看看下面的“EasySyMod”函数,因为它让我们引入了一个关键点:

    struct point *easy_mode(struct point *a, struct point *b) {
      a->x += b->x;
      a->y += b->y;
      return a;
    }
    
    如果现有的SWIG包装器尚未包装该函数,但确实包装了该结构,则该函数非常容易使用。我们可以简单地使用SWIG代码创建该结构的实例,并从中提取一个指针(通过ctypes)给导出但未包装的函数,例如:

    from ctypes import *
    from test import point
    
    p1 = point()
    p2 = point()
    p1.x = 123
    p1.y = 156
    p2.x = 123
    p2.y = 156
    
    so = CDLL('./_test.so')
    
    so.easy_mode(int(p1.this), int(p2.this))
    print(p1.x)
    print(p1.y)
    
    调用这个函数就足够了,正如我们所知,返回类型实际上只是修改
    p1
    ,我们可以使用这些知识并使用它。但要摆脱这一点,关键是调用
    int(p1.this)
    获取SWIG对象代理的指针的整数表示形式。这就是指针所需的所有ctypes

    让我们继续讨论这个问题,通过值传递和返回结构。这要困难得多,因为调用函数的方式取决于结构的大小和成员。它们的类型和顺序很重要。不同的体系结构会有所不同。在给定的体系结构中,它甚至会根据不同的情况而有所不同幸运的是,ctypes(通过libffi,这本身就是一件有趣的事情,如果你以前从未见过的话)对我们隐藏了所有这些

    现在我们的目标缺失函数可以是这样的:

    struct point add_them(struct point a, struct point b) {
      struct point ret = { a.x + b.x, a.y + b.y };
      return ret;
    }
    
    useable_size = CDLL(None).malloc_usable_size
    
    upper_size_bound = useable_size(int(p1.this))
    buffer_type = POINTER(c_char * upper_size_bound)
    print('Upper size bound is %d' % upper_size_bound)
    
    choose_type = dict([(1, c_uint8), (2, c_uint16), (4, c_uint32)]).get
    
    def generate_members(obj):
      for member_name in (x for x in dir(obj) if not x[0] == '_' and x != 'this'):
        print('Looking at member: %s' % member_name)
        def pos(shift):
          test = point()
          memset(int(test.this), 0, upper_size_bound)
          pattern = 0xff << (8 * shift)
          try:
            setattr(test, member_name, pattern)
          except:
            return -1
          return bytes(cast(int(test.this), buffer_type).contents).find(b'\xff')
        a=[pos(x) for x in range(upper_size_bound)]
        offset = min((x for x in a if x >= 0))
        size = 1 + max(a) - offset
        print('%s is a %d byte type at offset %d' % (member_name, size, offset))
        pad = [('pad', c_ubyte * offset)] if (offset > 0) else []
        class Member(Structure):
            _pack_ = 1
            _fields_ = pad + [(member_name, choose_type(size))]
    
        yield Member
    
    问题是,在只有一个不调用它的现有SWIG模块的场景中,我们对
    struct point
    的成员一无所知。这对于能够按值调用非常关键。当然,我们可以进行一些猜测,如果猜测足够简单,那么您也可以这样做,并为ctyp使用它是的

    幸运的是,结构的可用SWIG包装的存在给了我们(如果一些假设成立的话)足够对结构的类型/布局进行足够好的猜测。此外,由于我们知道如何获取指向结构实例使用的底层内存的指针,我们可以构造测试,向我们展示布局的相关内容。如果一切顺利,我们可以
    class YourType(Union):
        _pack_ = 1
        _fields_ = list(zip(string.ascii_lowercase, generate_members()))
        _anonymous_ = string.ascii_lowercase[:len(_fields_)]
    
    MyType=YourType
    
    y=MyType()
    y.x = 1 
    y.y = 2
    
    add_them = so.add_them
    add_them.argtypes = [MyType, MyType]
    add_them.restype = MyType
    
    v=add_them(y,y)
    print(v)
    print('V: %d,%d' % (v.x, v.y))