2016-08-28 7 views
0

У меня возникли проблемы с связью C++ (98) с python 3. У меня есть некоторые базовые классы в C++, которые я хотел бы расширить в Python. Некоторые рассматриваемые методы являются чисто виртуальными на стороне C++ и, таким образом, будут реализованы на стороне Python.Наследование базы из C++ в Python, вызов абстрактного метода с использованием SWIG

В настоящее время я могу назвать абстрактные методы из C++ и, по swig, специализация вызывается в Python. Круто. У меня возникли проблемы передав параметры в Python ..

Minimal полного примера, чтобы упростить свою задачу:

// 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" 

Run: как глоток

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

Compile myif_wrap.cxx -> _DllWrapper.pyd

Создайте * .exe со следующим кодом, он загрузит библиотеку _DllWrapper.pyd (убедитесь, что она находится в том же каталоге!). Кроме того, скопируйте файл DllWrapper.py, сгенерированный при помощи swig в каталог 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 

Выходной сигнал запуска ехе является:

__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? Как вы вызываете print (data.getA()) в классе python run()? В его нынешнем виде он ничего не печатает.

+0

Я подозреваю, что ваша проблема заключается в использовании shared_ptr. Работает ли он, когда вместо обычного указателя вы используете обычный тип данных? –

+1

сделайте * минимальный * и полный пример, который мы можем попробовать для себя, что иллюстрирует проблему. Без файлов заголовков (которые предположительно большие) я не могу отлаживать это сам. – Flexo

+0

Где вы определяете EMediaSampleMeta? Я бы ожидал, что% include «emediasamplemeta.h», но вы это забыли? –

ответ

0

Кажется, кто-то еще пробовал the exact same thing!

Что я сделал, это скомпилировать * .pyd с -DSWIG_TYPE_TABLE = iBase.

Затем я добавил это к основному приложению на стороне 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); 
} 

и называется форма реализации питона так:

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

прикончить, я составил реализацию C++ снова с -DSWIG_TYPE_TABLE = iBase.

Работает как очарование!

1

Это не совсем ответ, но я прочитал дискуссию Discussion from 2005, и имеет смысл, что это не должно быть возможным. Если вы на стороне Python, сделайте следующее, вы получите «разыменованное» перечисление на простое целое число.

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 

Я думаю, что проблема в том, что две разные виртуальные таблицы находятся в игре. Оригинальная C++ vtable и версия Swig Object. Любопытно, в каком сценарии интересен использовать потомок Python класса C++ из C++?

+0

Я хотел бы расширить базовый класс в python. Этот базовый класс имеет методы, которые принимают указатели на другие абстрактные типы, специализированные в C++. Поэтому в python я хотел бы назвать эти абстрактные методы обратно на C++. Это своего рода архитектура плагина Python для приложения на C++, если хотите. Я все еще изучаю его выполнимость. –

+0

Можно ли использовать описанную здесь стратегию, http://stackoverflow.com/questions/34445045/passing-python-functions-to-swig-wrapped-c-code. Тогда вы не можете использовать наследование, но вы должны работа с указателями функций. Ваш плагин, написанный на Python, реализует определенный набор функций с известными прототипами и вызывается из C++. Я использовал это, чтобы дать возможность экспериментировать с множеством функций (в виде плагинов) с большой программой, написанной на C++. –

+0

Хорошо, вы действительно можете сделать oop без наследования, но это еще одна история :-) Надеюсь, что вы найти способ добиться того, чего вы пытаетесь достичь. –