我有一个C++类容器,MyPase
下面是C++头代码( FrimeNo.H.EEM>)
#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
这就是来源(免费电话.cpp)
^{pr2}$请注意,这些对象存储在向量中的原始指针中。类就是这样设计的,因此容器负责释放其析构函数中的对象,就像在析构函数for循环中那样。在
<>在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模块(freemnot)并将其安装到python3的valid 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()返回时,如果thishown标志未设置为零,则对象o1的内存将被释放。在这行之后访问对象,使用返回的容器将导致灾难。请注意,这本身并没有什么问题,因为这就是Python垃圾收集的工作原理。在
对于上述用例,可以设置Python对象<强> > TysOb/StigabdFrase> EM>内的AdObjor函数,将使C++对象在Python中可用。 让用户设置此标志不是一个好的解决方案。 还可以使用“addObject”函数扩展python类,并在该函数中修改thisown标志,从而对用户隐藏这个内存技巧。在
问题是,如何让Swig做到这一点,而不扩展类? 我正在寻找使用类型映射,或者也许是%pythoncode,但我似乎找不到一个好的工作示例。在
上述代码将被调用并传递给调用Python解释器的C++程序。C++程序负责管理Python函数中分配的内存,即使在pyFalIZE()之后。在
以上代码可以从github https://github.com/TotteKarlsson/miniprojects下载
你在找%newobject。下面是一个小例子:
使用:
^{pr2}$有很多不同的方法可以解决这个问题,所以我将试着依次解释它们,并在此过程中建立一些东西。希望这对于了解SWIG的选项和内部结构非常有用,即使您只需要第一个示例。
添加Python代码以直接修改
thisown
最像您所建议的解决方案依赖于使用SWIG的
%pythonprepend
指令来添加一些额外的Python代码。你可以根据你关心的过载的C++声明来定位它,例如:唯一值得注意的怪癖来自这样一个事实:参数是使用
*args
而不是命名参数传入的,因此我们必须通过位置号来访问它。还有其他几个地方/方法可以在SWIG Python documentation中注入额外的Python代码(前提是不使用
-builtin
),monkey patching也是一种选择。使用Python的capi来调整
thisown
下一个可能的选择是使用typemap调用pythoncapi来执行等效的功能。在本例中,我匹配了参数类型和参数名,但这意味着这里的类型映射将应用于所有接收名为
^{pr2}$MyObject *
的函数。(这里最简单的解决方案是让名称描述头中的预期语义,如果这会超过当前的匹配,那么这样做的好处是使ide和文档更清晰)。除了typemap匹配之外,这个例子最值得注意的一点是在这里使用
$typemap
来“粘贴”另一个类型映射,特别是MyObject*
的默认类型映射到我们自己的类型映射中。有必要在生成的包装器文件中查看一下之前/之后的示例,看看结果是什么样的。使用SWIG运行时直接获取位于
SwigPyObject
结构的own
成员因为在Python代码中,我们已经编写了C++而不是通过^ {},所以可以使用这个类型映射来使用更多的SWIG内部结构,并且跳过从C到Python的往返,然后再返回C。
SWIG内部有一个结构,包含每个实例的详细信息,包括所有权、类型等
我们可以自己直接从
PyObject*
转换为SwigPyObject*
,但这需要编写错误处理/类型检查(这个PyObject甚至是SWIG对象吗?)并依赖于SWIG生成Python接口的各种不同方法的细节。相反,我们可以调用一个函数来为我们处理所有这些,所以我们现在可以这样编写我们的类型映射:这实际上只是前面答案的一个改进,但是纯粹是在swigc运行时实现的。
在添加之前复制构造一个新实例
还有其他方法可以解决这种所有权问题。首先,在这个特定的实例中,
MyContainer
假设它总是可以在它存储的每个实例上调用delete
(因此在这些语义中拥有)。如果我们还包装了一个这样的函数,那么这一点很有启发性:
这给我们以前的解决方案带来了一个问题-我们将
thisown
设置为0,但在这里它本来应该是0,因此在释放容器时,我们仍然无法合法地调用指针上的delete
。有一种简单的方法来处理这个问题,不需要知道SWIG代理的内部结构——假设
MyObject
是可复制的,那么你可以简单地创建一个新实例,并确保无论它来自何处,容器删除它都是合法的。我们可以通过稍微调整一下我们的类型图来做到这一点:这里要注意的是使用了更多的SWIG特性,这些特性让我们知道typemap输入的类型
$*1_type
是一次取消引用的typemap参数的类型。我们可以在这里写MyObject
,因为这是它的解决方案,但这让您可以处理这个问题ngs喜欢模板,如果你的容器真的是一个模板,或者用%apply
在其他类似的容器中重用typemap。现在注意的是如果你有一个C++函数,你就可以在没有假设{}的情况下返回一个实例,假设容器将占用现在不能保存的所有权。
给容器一个管理所有权的机会
最后,我喜欢使用的另一种技巧在这里是不可能直接使用的,但值得后人一提。{{24}你可以从cd13>调用一个额外的数据存储在cd13>中。如果在销毁时得到一个回调,那么还可以调用
Py_DECREF
,并强制Python运行时保持对象与容器一样长的存活时间。即使无法保持1-1
MyObject*
/PyObject*
配对,也可以通过在某处保持影子容器的活动来实现。这可能很难做到,除非您愿意将另一个对象添加到容器中,或者将其子类化,或者可以非常确定容器的初始Python实例将始终存在足够长的时间。相关问题 更多 >
编程相关推荐