添加swig pythoncode在Python对象上设置thisown标志
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了添加swig pythoncode在Python对象上设置thisown标志,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8799字,纯文字阅读大概需要13分钟。
内容图文
![添加swig pythoncode在Python对象上设置thisown标志](/upload/InfoBanner/zyjiaocheng/728/9cdc83110eba4bfcb7129d529dce1914.jpg)
我有一个swigged C类容器,MyContainer,持有MyObject类型的对象,也是一个C类.
以下是C头代码(freemenot.h)
#ifndef freemenotH
#define freemenotH
#include <vector>
#include <string>
using std::string;
class MyObject
{
public:
MyObject(const string& lbl);
~MyObject();
string getLabel();
private:
string label;
};
class MyContainer
{
public:
MyContainer();
~MyContainer();
void addObject(MyObject* o);
MyObject* getObject(unsigned int t);
int getNrOfObjects();
private:
std::vector<MyObject*> mObjects;
};
#endif
这是源(freemenot.cpp)
#include "freemenot.h"
#include <iostream>
using namespace std;
/* MyObject source */
MyObject::MyObject(const string& lbl)
:
label(lbl)
{ cout<<"In object ctor"<<endl; }
MyObject::~MyObject() { cout<<"In object dtor"<<endl; }
string MyObject::getLabel() { return label; }
/* MyContainer source */
MyContainer::MyContainer() { cout<<"In container ctor"<<endl; }
MyContainer::~MyContainer()
{
cout<<"In container dtor"<<endl;
for(unsigned int i = 0; i < mObjects.size(); i++)
{
delete mObjects[i];
}
}
int MyContainer::getNrOfObjects() { return mObjects.size(); }
void MyContainer::addObject(MyObject* o) { mObjects.push_back(o); }
MyObject* MyContainer::getObject(unsigned int i) { return mObjects[i]; }
观察对象在向量中存储为RAW POINTERS.这个类是这样设计的,因此容器负责释放析构函数中的对象,就像在循环的析构函数中完成一样.
在C代码中,如下所示,将对象o1添加到容器c中,该对象返回到客户端代码
MyContainer* getAContainerWithSomeObjects()
{
MyContainer* c = new MyContainer();
MyObject* o1 = new MyObject();
c.add(o1);
return c;
}
返回的容器拥有其对象,并在完成后负责取消分配这些对象.在C中,在函数退出之后访问容器对象就可以了.
使用Swig将上述类公开给python将需要一个接口文件.此接口文件如下所示
%module freemenot
%{ #include "freemenot.h" %}
%include "std_string.i"
//Expose to Python
%include "freemenot.h"
为了使用CMake生成Python模块,使用了以下CMake脚本.
cmake_minimum_required(VERSION 2.8)
project(freemenot)
find_package(SWIG REQUIRED)
include(UseSWIG)
find_package(PythonInterp)
find_package(PythonLibs)
get_filename_component(PYTHON_LIB_FOLDER ${PYTHON_LIBRARIES} DIRECTORY CACHE)
message("Python lib folder: " ${PYTHON_LIB_FOLDER})
message("Python include folder: " ${PYTHON_INCLUDE_DIRS})
message("Python libraries: " ${PYTHON_LIBRARIES})
set(PyModule "freemenot")
include_directories(
${PYTHON_INCLUDE_PATH}
${CMAKE_CURRENT_SOURCE_DIR}
)
link_directories( ${PYTHON_LIB_FOLDER})
set(CMAKE_MODULE_LINKER_FLAGS ${CMAKE_CURRENT_SOURCE_DIR}/${PyModule}.def)
set_source_files_properties(${PyModule}.i PROPERTIES CPLUSPLUS ON)
set_source_files_properties(${PyModule}.i PROPERTIES SWIG_FLAGS "-threads")
SWIG_ADD_LIBRARY(${PyModule}
MODULE LANGUAGE python
SOURCES ${PyModule}.i freemenot.cpp)
SWIG_LINK_LIBRARIES (${PyModule} ${PYTHON_LIB_FOLDER}/Python37_CG.lib )
# INSTALL PYTHON BINDINGS
# Get the python site packages directory by invoking python
execute_process(COMMAND python -c "import site; print(site.getsitepackages()[0])" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
message("PYTHON_SITE_PACKAGES = ${PYTHON_SITE_PACKAGES}")
install(
TARGETS _${PyModule}
DESTINATION ${PYTHON_SITE_PACKAGES})
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PyModule}.py
DESTINATION ${PYTHON_SITE_PACKAGES}
)
使用CMake生成make文件,并使用borlands bcc32编译器进行编译,生成Python模块(freemenot)并将其安装到python3有效的sitepackages文件夹中.
然后,在Python中,可以使用以下脚本来说明问题
import freemenot as fmn
def getContainer():
c = fmn.MyContainer()
o1 = fmn.MyObject("This is a label")
o1.thisown = 0
c.addObject(o1)
return c
c = getContainer()
print (c.getNrOfObjects())
#if the thisown flag for objects in the getContainer function
#is equal to 1, the following call return an undefined object
#If the flag is equal to 0, the following call will return a valid object
a = c.getObject(0)
print (a.getLabel())
这个Python代码可能看起来很好,但不能按预期工作.问题是,当函数getContainer()返回时,如果thisown标志未设置为零,则释放对象o1的内存.使用返回的容器访问该行之后的对象将最终导致灾难.请注意,这本身没有任何问题,因为这是pythons垃圾收集的工作原理.
对于上面的用例,能够在addObject函数中设置python对象thisown标志,将呈现在Python中可用的C对象.
让用户设置此标志不是一个好的解决方案.
还可以使用“addObject”函数扩展python类,并修改此函数内的thisown标志,从而隐藏用户的这种内存技巧.
问题是,如何在没有扩展课程的情况下让Swig这样做?
我正在寻找使用类型图,或者可能是%pythoncode,但我似乎无法找到一个好的工作示例.
上面的代码将被一个调用Python解释器的C程序使用并传递给它. C程序负责管理python函数中分配的内存,即使在PyFinalize()之后也是如此.
上面的代码可以从github https://github.com/TotteKarlsson/miniprojects下载
解决方法:
有许多不同的方法可以解决这个问题,所以我会尝试依次解释它们,并在此过程中构建一些东西.希望这对于SWIG的选项和内部结构非常有用,即使您只需要第一个示例.
添加Python代码以直接修改thisown
与您提出的解决方案最相似的解决方案依赖于使用SWIG的%pythonprepend指令来添加一些额外的Python代码.您可以根据您关注的重载的C声明来定位它,例如:
%module freemenot
%{ #include "freemenot.h" %}
%include "std_string.i"
%pythonprepend MyContainer::addObject(MyObject*) %{
# mess with thisown
print('thisown was: %d' % args[0].thisown)
args[0].thisown = 0
%}
//Expose to Python
%include "freemenot.h"
唯一值得注意的怪癖来自于使用* args而不是命名参数传递参数这一事实,因此我们必须通过位置编号访问它.
在SWIG Python documentation中还有其他一些地方/方法可以注入额外的Python代码(前提是你没有使用-builtin),猴子修补也总是一个选项.
使用Python的C API来调整thisown
这里的下一个可能选择是使用typemap调用Python C API来执行等效功能.在这个例子中,我匹配了参数类型和参数名称,但这确实意味着这里的typemap将应用于接收名为o的MyObject *的所有函数. (这里最简单的解决方案是使名称描述标题中的预期语义,如果它当前过度匹配,这有利于使IDE和文档更清晰).
%module freemenot
%{ #include "freemenot.h" %}
%include "std_string.i"
%typemap(in) MyObject *o {
PyObject_SetAttrString($input, "thisown", PyInt_FromLong(0)); // As above, but C API
$typemap(in,MyObject*); // use the default typemap
}
//Expose to Python
%include "freemenot.h"
除了类型映射匹配之外,关于此示例最值得注意的一点是使用$typemap来粘贴另一个类型映射,特别是MyObject *的默认类型映射到我们自己的类型映射中.值得查看生成的包装器文件内部的前/后示例,看起来是什么样子.
使用SWIG运行时直接获取SwigPyObject struct自己的成员
由于我们已经编写了C而不是通过Python代码中的setattr,我们可以调整此类型映射以使用更多SWIG的内部结构,并跳过从C到Python的往返并再次返回到C.
在SWIG内部,有一个结构,其中包含每个实例的详细信息,包括所有权,类型等.
我们可以直接从PyObject *转换为SwigPyObject *,但是这需要自己编写错误处理/类型检查(这个PyObject甚至是SWIG吗?)并且依赖于SWIG可以生成Python接口的各种不同方式的细节.相反,我们可以调用一个函数来处理所有这些函数,所以我们现在可以像这样编写类型图:
%module freemenot
%{ #include "freemenot.h" %}
%include "std_string.i"
%typemap(in) MyObject *o {
// TODO: handle NULL pointer still
SWIG_Python_GetSwigThis($input)->own = 0; // Safely cast $input from PyObject* to SwigPyObject*
$typemap(in,MyObject*); // use the default typemap
}
//Expose to Python
%include "freemenot.h"
这只是前一个答案的演变,但纯粹在SWIG C运行时实现.
Copy在添加之前构造一个新实例
还有其他方法可以解决这种所有权问题.首先,在这个特定的实例中,你的MyContainer假设它总是可以在它存储的每个实例上调用delete(因此拥有这些语义).
对此的激励示例是,如果我们也包装这样的函数:
MyObject *getInstanceOfThing() {
static MyObject a;
return &a;
}
这引入了我们之前的解决方案的问题 – 我们将this设置为0,但是这里它已经是0,所以当释放容器时我们仍然不能合法地调用指针上的delete.
有一种简单的方法可以解决这个问题,不需要了解SWIG代理内部结构 – 假设MyObject是可复制的,那么你可以简单地创建一个新的实例,并确保无论它来自何处它都是合法的容器删除它.我们可以通过稍微调整我们的类型图来做到这一点:
%module freemenot
%{ #include "freemenot.h" %}
%include "std_string.i"
%typemap(in) MyObject *o {
$typemap(in,MyObject*); // use the default typemap as before
$1 = new $*1_type(*$1); // but afterwards call copy-ctor
}
//Expose to Python
%include "freemenot.h"
这里需要注意的是使用了几个SWIG功能,让我们知道了typemap输入的类型 – $* 1_type是取消引用一次的typemap参数的类型.我们可以在这里编写MyObject,就像它解决的那样,但是如果你的容器真的是模板,你可以处理模板之类的东西,或者在%apply的其他类似容器中重复使用typemap.
现在要注意的事情是泄漏,如果你有一个C函数,你故意允许返回一个实例,而不假设容器会占用现在不能拥有的所有权.
为容器提供管理所有权的机会
最后,我喜欢使用的其他技术之一在目前所提出的并不是直接可行的,但值得一提的是后人.如果你有机会在容器中的每个实例旁边存储一些额外的数据,你可以调用Py_INCREF并保留对底层PyObject *的引用,无论它来自何处.如果您在销毁时获得回调,您也可以调用Py_DECREF并强制Python运行时使对象保持活动状态与容器一样长.
你也可以做到这一点,即使不能保持1-1 MyObject * / PyObject *配对活着,也可以保持阴影容器在某处活着.除非您愿意将另一个对象添加到容器中,对其进行子类化或者可以非常确定容器的初始Python实例将始终存活足够长,否则这很难做到.
内容总结
以上是互联网集市为您收集整理的添加swig pythoncode在Python对象上设置thisown标志全部内容,希望文章能够帮你解决添加swig pythoncode在Python对象上设置thisown标志所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。