在Python中深度复制C结构的SWIG包装器。

问题描述 投票:1回答:1

我正在使用 SWIG 为一个 C 库生成 Python 绑定。该库定义了一个具有值语义的结构。在 C++ 术语中,这个结构应该是 POD - 将它用 memcpy 产生一个语义正确的副本。

clib.h。

struct s
{
     int i;
};

我用SWIG将其编译成Python模块。构建过程的细节在本题的 "附录 "中。

在C代码中,我们可以验证结构类型的变量的赋值操作符是否具有值语义。

#include <assert.h>
#include "clib.h"

int main()
{
    struct s s1;
    s1.i = 100;

    struct s s2 = s1;
    assert(s2.i == 100);

    s2.i = 101;
    assert(s1.i == 100);
}

在Python包装器中,正如我们所期望的那样,我们反而有引用语义。

import clib

s1 = clib.s()
s1.i = 100

s2 = s1
assert s2.i == 100

s2.i = 101
assert s1.i == 101

如果我们的库是用C++写的,并且 s 有一个复制构造函数,SWIG也会为Python包装器生成一个(参考). 我们可以写。

s3 = clib.s(s1)

但对于一个C库来说,这个封装器是不会生成的。

TypeError: __init__() takes exactly 1 argument (2 given)

我们可能会希望SWIG能够生成相应的神奇方法,用于 copy.deepcopy:

from copy import deepcopy
s4 = deepcopy(s1)

但它没有。

TypeError: can't pickle SwigPyObject objects

我很困惑。这似乎应该很简单,但我什么也找不到。在 SWIG和Python "文档拷贝 "这个词只出现在前面链接的C++拷贝构造函数的说明中,以及一些关于生成的包装代码的低级细节中。拷贝 "这个词只出现在前面链接的C++拷贝构造函数的说明中,以及一些关于生成的封装代码的低级细节中。最接近的问题在Stack Overflow上 有一个复杂得可怕的答案。


细节再现

定义一个简单的SWIG接口文件。clib.i:

%module clib

%{
#include "clib.h"
%}

%include "clib.h"

创建一个Python模块,使用 setup.py:

from distutils.core import setup, Extension

clib = Extension(
    "_clib",
    sources=["clib_wrap.c"],
    extra_compile_args=["-g"],
)

setup(name="clib", version="1.0", ext_modules=[clib])

构建整个事情与一个 Makefile:

swig: setup.py clib_wrap.c
    python2 setup.py build_ext --inplace

clib_wrap.c: clib.i
    swig -python clib.i

然后,编译Python和C测试程序,完全按照上面列出的方法。

python c swig
1个回答
1
投票

虽然一个C库不能有构造函数(constructorsdestructors),但你可以在事后为SWIG包装器定义它们 参考:Swig docs 5.5.6。. 注意,构造函数一定要认真写。

有一个与普通C++构造函数实现的微妙区别 就是尽管构造函数声明与普通C++构造函数一样,但新构造的对象必须被返回,就像构造函数声明有一个返回值一样。

test.i:

%module test

%{
#include <stdlib.h>
#include "clib.h"
%}

%include "clib.h"

%extend s {
    s(int i) {
        struct s* t = malloc(sizeof(struct s));
        t->i = i;
        return t;
    }
    s(struct s* o) {
        struct s* t = malloc(sizeof(struct s));
        t->i = o->i;
        return t;
    }
    ~s() {
        free($self);
    }
}

用例:

>>> import test
>>> s1 = test.s(5)
>>> s1.i
5
>>> s2 = test.s(s1)  # copy
>>> s2.i
5
>>> s2.i = 7
>>> s1.i
5
>>> s2.i
7
© www.soinside.com 2019 - 2024. All rights reserved.