SWIG:避免 C++-to-Python 别名
SWIG: avoid C++-to-Python aliasing
我正在用 C++ 构建一个库,它使用 SWIG 4.0.1
连接到 Python 3。经过一段时间的开发后,我注意到(我认为是所谓的)别名问题。我准备了一个最小的例子,这也会发生。
考虑具有单个(私有)属性 attr
的 class dummy
,该属性在 0
处初始化。现在,我有这个非常小的 python 脚本:
import dummy_wrap
d1 = dummy_wrap.dummy()
d2 = d1
d1.set_attr(12)
print(d2.get_attr()) # this prints '12', not '0'
我希望我所说的别名是这里真正发生的事情:object d2
从未被修改,但它采用赋予 d1
的值,就好像赋值一样d2=d1
实际上是指针赋值。我希望发生的(也是我期望发生的)是赋值运算符 a=b
制作 b
的硬拷贝并将其传递给 a
。换句话说,对 a
的修改不应影响 b
,反之亦然,只要这符合实现细节(就像我的库和最小示例中的情况一样)。
最后一点,这是我在编译时收到的消息:
g++ -fPIC -c dummy.cpp
g++ -fPIC -shared -o libdummy_lib.so dummy.o
swig -Wall -c++ -python -py3 -o dummy_wrap.cxx dummy_wrap.i
dummy.hpp:10: Warning 362: operator= ignored
dummy.hpp:11: Warning 362: operator= ignored
dummy.hpp:7: Warning 509: Overloaded method dummy::dummy(dummy &&) effectively ignored,
dummy.hpp:6: Warning 509: as it is shadowed by dummy::dummy(dummy const &).
g++ -fPIC -c dummy_wrap.cxx -I /usr/include/python3.8
g++ -fPIC -shared -o _dummy_wrap.so dummy_wrap.o -L . -ldummy_lib -lpython3.8
问题:
- 我想了解这里发生了什么以及为什么:
dummy(dummy&&)
和 dummy& operator=(dummy&&)
会影响 SWIG 包装 dummy(const dummy&)
和 dummy& operator=(const dummy&)
的方式吗?
- 期望我想要的默认行为是否合理,即,这是(别名)使用 SWIG 连接到 python 后 class 的预期行为吗?
- 我该如何解决这个问题:我应该做哪些更改才能让操作员
=
制作硬拷贝?
非常感谢。
为了复制这个,class header dummy.hpp
是:
#pragma once
class dummy {
public:
dummy() = default;
dummy(const dummy&) = default;
dummy(dummy&&) = default;
~dummy() = default;
dummy& operator=(const dummy&) = default;
dummy& operator=(dummy&&) = default;
void set_attr(int v);
int get_attr() const;
private:
int attr = 0;
};
而 class 实现 dummy.cpp
只是
#include "dummy.hpp"
void dummy::set_attr(int v) { attr = v; }
int dummy::get_attr() const { return attr; }
接口文件dummy_wrap.i
是:
%module dummy_wrap
%{
#include "dummy.hpp"
%}
%include "dummy.hpp"
我用来编译的Makefile
和link一切都是
all: libdummy_lib.so _dummy_wrap.so
_dummy_wrap.so: dummy_wrap.o
g++ -fPIC -shared -o _dummy_wrap.so dummy_wrap.o -L . -ldummy_lib -lpython3.8
dummy_wrap.o: dummy_wrap.cxx
g++ -std=c++17 -fPIC -c dummy_wrap.cxx -I /usr/include/python3.8
dummy_wrap.cxx: dummy_wrap.i
swig -Wall -c++ -python -py3 -o dummy_wrap.cxx dummy_wrap.i
libdummy_lib.so: dummy.o
g++ -fPIC -shared -o libdummy_lib.so dummy.o
dummy.o: dummy.cpp dummy.hpp
g++ -std=c++17 -fPIC -c dummy.cpp
clean:
rm -f libdummy_lib.so dummy.o
rm -f _dummy_wrap.so dummy_wrap.o dummy_wrap.cxx dummy_wrap.py
在 linux 中,记得更新 LD_LIBRARY_PATH
使用:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
因此,感谢原 post 中的一些评论,一个可能的解决方案是添加一个 clone()
方法。可以通过简单地扩展 C++ header 来做到这一点。但是,由于这对某些人来说可能看起来很奇怪,因为在许多情况下根本不需要 C++ 中的 clone()
方法,我们可以使用 SWIG 扩展 python class。只需将示例中的 dummy_wrap.i
并在文件末尾添加以下代码
%extend dummy {
dummy clone() const {
return *$self;
}
}
不幸的是,这还没有结束,因为我们的 python 代码必须修改:
import dummy_wrap
d1 = dummy_wrap.dummy()
d2 = d1.clone()
d1.set_attr(12)
print(d2.get_attr()) # now this prints '0'
我正在用 C++ 构建一个库,它使用 SWIG 4.0.1
连接到 Python 3。经过一段时间的开发后,我注意到(我认为是所谓的)别名问题。我准备了一个最小的例子,这也会发生。
考虑具有单个(私有)属性 attr
的 class dummy
,该属性在 0
处初始化。现在,我有这个非常小的 python 脚本:
import dummy_wrap
d1 = dummy_wrap.dummy()
d2 = d1
d1.set_attr(12)
print(d2.get_attr()) # this prints '12', not '0'
我希望我所说的别名是这里真正发生的事情:object d2
从未被修改,但它采用赋予 d1
的值,就好像赋值一样d2=d1
实际上是指针赋值。我希望发生的(也是我期望发生的)是赋值运算符 a=b
制作 b
的硬拷贝并将其传递给 a
。换句话说,对 a
的修改不应影响 b
,反之亦然,只要这符合实现细节(就像我的库和最小示例中的情况一样)。
最后一点,这是我在编译时收到的消息:
g++ -fPIC -c dummy.cpp
g++ -fPIC -shared -o libdummy_lib.so dummy.o
swig -Wall -c++ -python -py3 -o dummy_wrap.cxx dummy_wrap.i
dummy.hpp:10: Warning 362: operator= ignored
dummy.hpp:11: Warning 362: operator= ignored
dummy.hpp:7: Warning 509: Overloaded method dummy::dummy(dummy &&) effectively ignored,
dummy.hpp:6: Warning 509: as it is shadowed by dummy::dummy(dummy const &).
g++ -fPIC -c dummy_wrap.cxx -I /usr/include/python3.8
g++ -fPIC -shared -o _dummy_wrap.so dummy_wrap.o -L . -ldummy_lib -lpython3.8
问题:
- 我想了解这里发生了什么以及为什么:
dummy(dummy&&)
和dummy& operator=(dummy&&)
会影响 SWIG 包装dummy(const dummy&)
和dummy& operator=(const dummy&)
的方式吗? - 期望我想要的默认行为是否合理,即,这是(别名)使用 SWIG 连接到 python 后 class 的预期行为吗?
- 我该如何解决这个问题:我应该做哪些更改才能让操作员
=
制作硬拷贝?
非常感谢。
为了复制这个,class header dummy.hpp
是:
#pragma once
class dummy {
public:
dummy() = default;
dummy(const dummy&) = default;
dummy(dummy&&) = default;
~dummy() = default;
dummy& operator=(const dummy&) = default;
dummy& operator=(dummy&&) = default;
void set_attr(int v);
int get_attr() const;
private:
int attr = 0;
};
而 class 实现 dummy.cpp
只是
#include "dummy.hpp"
void dummy::set_attr(int v) { attr = v; }
int dummy::get_attr() const { return attr; }
接口文件dummy_wrap.i
是:
%module dummy_wrap
%{
#include "dummy.hpp"
%}
%include "dummy.hpp"
我用来编译的Makefile
和link一切都是
all: libdummy_lib.so _dummy_wrap.so
_dummy_wrap.so: dummy_wrap.o
g++ -fPIC -shared -o _dummy_wrap.so dummy_wrap.o -L . -ldummy_lib -lpython3.8
dummy_wrap.o: dummy_wrap.cxx
g++ -std=c++17 -fPIC -c dummy_wrap.cxx -I /usr/include/python3.8
dummy_wrap.cxx: dummy_wrap.i
swig -Wall -c++ -python -py3 -o dummy_wrap.cxx dummy_wrap.i
libdummy_lib.so: dummy.o
g++ -fPIC -shared -o libdummy_lib.so dummy.o
dummy.o: dummy.cpp dummy.hpp
g++ -std=c++17 -fPIC -c dummy.cpp
clean:
rm -f libdummy_lib.so dummy.o
rm -f _dummy_wrap.so dummy_wrap.o dummy_wrap.cxx dummy_wrap.py
在 linux 中,记得更新 LD_LIBRARY_PATH
使用:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
因此,感谢原 post 中的一些评论,一个可能的解决方案是添加一个 clone()
方法。可以通过简单地扩展 C++ header 来做到这一点。但是,由于这对某些人来说可能看起来很奇怪,因为在许多情况下根本不需要 C++ 中的 clone()
方法,我们可以使用 SWIG 扩展 python class。只需将示例中的 dummy_wrap.i
并在文件末尾添加以下代码
%extend dummy {
dummy clone() const {
return *$self;
}
}
不幸的是,这还没有结束,因为我们的 python 代码必须修改:
import dummy_wrap
d1 = dummy_wrap.dummy()
d2 = d1.clone()
d1.set_attr(12)
print(d2.get_attr()) # now this prints '0'