Python 可以组合swig生成的数据类型和ctypes函数
我正在为C库编写一个带有ctypes的python包装器,我有一个返回结构的C函数。但该结构是在另一个C文件中定义的,该文件由swig包装 我简化了结构和代码 该结构由swig包裹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
结构点{
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<代码>接口。
您可能会找到信息丰富的答案。完全有可能让SWIG和CType以各种不同的方式协同工作,我将在下面的几个例子中展示。不过,有一个很大的警告:根据我的经验,它几乎永远不会为您提供可维护、可移植、可扩展等功能。因此,我倾向于如果可能的话,ed会倾向于其他选择(这些可能包括使用任何合适的说服手段向原始作者索要资料来源,从头开始重新编写整个内容,或者简单地使用其他库) 无论如何,让我们假设您已经为给定的Python版本构建了一个SWIG包装库。无论出于何种原因,它们都没有包装您最喜欢的函数。因此,您只需要使用ctypes将某些东西修补到SWIG生成的代码中。如果:
你的情况并不完全符合这些限制,但是为了进行一个预热练习,让我们来看看下面的“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))