在Python中继承C ++的Base,使用SWIG调用抽象方法

时间:2016-08-28 09:02:12

标签: python c++ swig

我在将C ++(98)与python 3结合起来时遇到了一些麻烦。我在C ++中有一些基类,我想用Python扩展它们。有些方法在C ++方面是纯虚拟的,因此将在Python方面实现。

目前,我可以从C ++调用抽象方法,并且在swig中,在Python中调用特化。凉。我无法将参数移交给Python ..

简化完整示例以简化我的问题:

// iBase.h
#pragma once
#include <memory>

typedef enum EMyEnumeration{
    EMyEnumeration_Zero,
    EMyEnumeration_One,
    EMyEnumeration_Two

}TEMyEnumeration;


class FooBase{
protected:  
    int a;
public:
    virtual int getA() = 0 ; 
};

class Foo : public FooBase{
public:
    Foo() {a = 2;}
    int getA(){return a;}
};

class iBase{
    public:

    virtual void start() =0;
    virtual void run(std::shared_ptr<FooBase> p, TEMyEnumeration enumCode) = 0;
};

在swig方面:

// myif.i
%module(directors="1") DllWrapper

%{
#include <iostream>
#include "iBase.h"
%}

%include <std_shared_ptr.i>
%shared_ptr(FooBase)
%shared_ptr(Foo)
%feature("director") FooBase;
%feature("director") iBase;
%include "iBase.h"

运行swig as:

swig -c++ -python myif.i
swig -Wall -c++ -python -external-runtime runtime.h

编译myif_wrap.cxx - &gt; _DllWrapper.pyd

使用以下代码创建* .exe,它将加载_DllWrapper.pyd库(确保它位于同一目录中!)。另外,将swig生成的DllWrapper.py复制到exe目录。

//Main_SmartPtr.cpp
#include "stdafx.h"
#include <Python.h>
#include <windows.h> 
#include <string>
#include <memory>
#include "iBase.h"
#include "runtime.h"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    string moduleName = "ExampleSmartPtr";

    // load *.pyd  (actually a dll file which implements PyInit__<swigWrapperName>)
    auto handle =LoadLibrary("_DllWrapper.pyd");

    // getting an instance handle..
    Py_Initialize();
    PyObject *main = PyImport_AddModule("__main__");
    PyObject *dict = PyModule_GetDict(main);

    PyObject *module = PyImport_Import(PyString_FromString(moduleName.c_str()));
    PyModule_AddObject(main, moduleName.c_str(), module);
    PyObject *instance = PyRun_String(string(moduleName+string(".")+moduleName+string("()")).c_str(), Py_eval_input, dict, dict);


    //calling start() in the Python derived class..
    //PyObject *result = PyObject_CallMethod(instance, "start", (char *)"()");

    // trying to call run in the Python derived class..

    shared_ptr<Foo> foo = make_shared<Foo>();
    EMyEnumeration enumCode = EMyEnumeration_Two;


    string  typeName1 = "std::shared_ptr <FooBase> *"; 
    swig_type_info* info1 = SWIG_TypeQuery(typeName1.c_str());
    auto swigData1 = SWIG_NewPointerObj((void*)(&foo), info1, SWIG_POINTER_OWN);

    string  typeName2 = "TEMyEnumeration *"; 
    swig_type_info* info2 = SWIG_TypeQuery(typeName2.c_str());
    auto swigData2 = SWIG_NewPointerObj((void*)(&enumCode), info2, SWIG_POINTER_OWN);

    auto result = PyObject_CallMethod(instance, "run", (char *)"(O)(O)", swigData1, swigData2);

    return 0;
}

创建一个新的Python文件并将其放在exe的目录中:

#ExampleSmartPtr.py
import DllWrapper

class ExampleSmartPtr(DllWrapper.iBase):
    def __init__(self): # constructor
        print("__init__!!")
        DllWrapper.iBase.__init__(self)

    def start(self):
        print("start")
        return 0

    def run(self, data, enumCode):
        print("run")
        print("-> data: "+str(data))
        print("-> enumCode: "+str(enumCode))

        print (data.getA())
        return 1

运行exe的输出是:

__init__!!
run
-> data: (<DllWrapper.FooBase; proxy of <Swig Object of type 'std::shared_ptr< FooBase > *' at 0x00000000014F8B70> >,)
-> enumCode: (<Swig Object of type 'TEMyEnumeration *' at 0x00000000014F89F0>,)

如何将enumCode'取消引用'到一个简单的int?如何在python类run()中调用print(data.getA())?在目前的形式,它不打印任何东西..

2 个答案:

答案 0 :(得分:1)

这不是一个真正的答案,但我阅读了讨论Discussion from 2005,这是有道理的,它应该是不可能的。如果您在Python方面,请执行以下操作,您将获得枚举&#39; dereferenced&#39;到一个简单的整数。

import ExampleSmartPtr

instance = ExampleSmartPtr.ExampleSmartPtr()
swigData1 = ExampleSmartPtr.DllWrapper.Foo()
swigData2 = ExampleSmartPtr.DllWrapper.EMyEnumeration_Two
instance.run(swigData1,swigData2)

这将打印

__init__!!
run
-> data: <DllWrapper.Foo; proxy of <Swig Object of type 'std::shared_ptr< Foo > *' at 0x7f8825c0b7e0> >
-> enumCode: 2

我认为问题在于两个不同的vtable正在发挥作用。原始的C ++ vtable和Swig Object的vtable。只是好奇,在C ++中使用C ++类的Python后代在什么情况下感兴趣?

答案 1 :(得分:0)

似乎有人试过the exact same thing

我所做的是使用-DSWIG_TYPE_TABLE = iBase编译* .pyd。

然后我将其添加到cpp端的主应用程序中:

iBase *python2interface(PyObject *obj) {
  void *argp1 = 0;
  swig_type_info * pTypeInfo = SWIG_TypeQuery("iBase *");

  const int res = SWIG_ConvertPtr(obj, &argp1,pTypeInfo, 0);
  if (!SWIG_IsOK(res)) {
    abort();
  }
  return reinterpret_cast<iBase*>(argp1);
}

并像这样调用python的实现形式:

auto foo = make_shared<Foo>();
TEMyEnumeration enumCode = EMyEnumeration_Two;
python2interface(instance)->run(foo, enumCode);

为了完成,我用-DSWIG_TYPE_TABLE = iBase再次编译了C ++实现。

像魅力一样!