添加swigpythoncode以在Python obj上设置thisown标志

2024-05-19 03:39:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个C++类容器,MyPase,持有类型 MyObjult的对象,也是C++类。在

下面是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下载


Tags: the对象函数代码stringreturnincludepackages
2条回答

你在找%newobject。下面是一个小例子:

%module test

%newobject create;
%delobject destroy;

%inline %{

#include <iostream>
struct Test
{
    Test() { std::cout << "create" << std::endl; }
    ~Test() { std::cout << "destroy" << std::endl; }
};

Test* create() { return new Test; }
void destroy(Test* t) { delete t; }

%}

使用:

^{pr2}$

有很多不同的方法可以解决这个问题,所以我将试着依次解释它们,并在此过程中建立一些东西。希望这对于了解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),monkey patching也是一种选择。

使用Python的capi来调整thisown

下一个可能的选择是使用typemap调用pythoncapi来执行等效的功能。在本例中,我匹配了参数类型参数名,但这意味着这里的类型映射将应用于所有接收名为MyObject *的函数。(这里最简单的解决方案是让名称描述头中的预期语义,如果这会超过当前的匹配,那么这样做的好处是使ide和文档更清晰)。

^{pr2}$

除了typemap匹配之外,这个例子最值得注意的一点是在这里使用$typemap来“粘贴”另一个类型映射,特别是MyObject*的默认类型映射到我们自己的类型映射中。有必要在生成的包装器文件中查看一下之前/之后的示例,看看结果是什么样的。

使用SWIG运行时直接获取位于SwigPyObject结构的own成员

因为在Python代码中,我们已经编写了C++而不是通过^ {},所以可以使用这个类型映射来使用更多的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"

这实际上只是前面答案的一个改进,但是纯粹是在swigc运行时实现的。

在添加之前复制构造一个新实例

还有其他方法可以解决这种所有权问题。首先,在这个特定的实例中,MyContainer假设它总是可以在它存储的每个实例上调用delete(因此在这些语义中拥有)。

如果我们还包装了一个这样的函数,那么这一点很有启发性:

MyObject *getInstanceOfThing() {
    static MyObject a;
    return &a;
}

这给我们以前的解决方案带来了一个问题-我们将thisown设置为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,因为这是它的解决方案,但这让您可以处理这个问题ngs喜欢模板,如果你的容器真的是一个模板,或者用%apply在其他类似的容器中重用typemap。

现在注意的是如果你有一个C++函数,你就可以在没有假设{ }的情况下返回一个实例,假设容器将占用现在不能保存的所有权。

给容器一个管理所有权的机会

最后,我喜欢使用的另一种技巧在这里是不可能直接使用的,但值得后人一提。{{24}你可以从cd13>调用一个额外的数据存储在cd13>中。如果在销毁时得到一个回调,那么还可以调用Py_DECREF,并强制Python运行时保持对象与容器一样长的存活时间。

即使无法保持1-1MyObject*/PyObject*配对,也可以通过在某处保持影子容器的活动来实现。这可能很难做到,除非您愿意将另一个对象添加到容器中,或者将其子类化,或者可以非常确定容器的初始Python实例将始终存在足够长的时间。

相关问题 更多 >

    热门问题