python - C 结构体的 Python SWIG 包装器中的深度复制

标签 python c swig

我正在使用 SWIG 为 C 库生成 Python 绑定(bind)。该库定义了具有值语义的结构。在 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 包装器生成一个复制构造函数 ( reference )。我们可以这样写:

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

我很困惑。这看起来应该很简单,但我什么也没找到。在 the "SWIG and Python" documentation ,“复制”一词仅出现在之前链接的有关 C++ 复制构造函数的注释中,以及有关生成的包装器代码的一些低级详细信息中。 closest question on Stack Overflow有一个极其复杂的答案。


重现详细信息

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

%module clib

%{
#include "clib.h"
%}

%include "clib.h"

使用setup.py创建Python模块:

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 测试程序。

最佳答案

虽然 C 库不能有构造函数/析构函数,但您可以事后为 SWIG 包装器定义它们 ref: swig docs 5.5.6 。请注意,必须仔细编写构造函数:

There is one subtle difference to a normal C++ constructor implementation though and that is although the constructor declaration is as per a normal C++ constructor, the newly constructed object must be returned as if the constructor declaration had a return value.

测试.i:

%module test

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

%include "clib.h"

%extend s {       // add additional methods to the s struct
    s(int i) {    // constructor
        struct s* t = malloc(sizeof(struct s));
        t->i = i;
        return t;
    }
    s(struct s* o) { // copy constructor
        struct s* t = malloc(sizeof(struct s));
        t->i = o->i;
        return t;
    }
    ~s() {           // destructor
        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

关于python - C 结构体的 Python SWIG 包装器中的深度复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61763169/

相关文章:

python - Keras 多类模型尺寸错误

获取包含整数和 float 的列表最大值的Python方法

python - 类型错误 : object of type 'builtin_function_or_method' has no len()

c - 为什么 gets() 比 scanf() 更危险?

python - libccv - 如何从内存中的字节读取图像

python - 将 .distinct() 与 MySQL 一起使用

c - 我的字符、单词、行数计数程序有什么问题?

c - Pthreads 与 OpenMP

python - 将手工包装方法添加到 Swig 输出

python - swig c++ 到 python (with numpy) : error: use of undeclared identifier 'import_array'