这个答案分为两部分。首先,您需要以一种方式在Python中公开您的接口,使得Python实现可以随意重写其中的某些部分。然后,您需要展示如何让C++程序(在main
中)调用Python。
将现有接口暴露给Python:
第一部分可以使用SWIG很容易地完成。我稍微修改了您的示例场景以修复一些问题,并添加了一个额外的测试函数:
class myif {
public:
virtual float myfunc(float a) = 0;
};
inline void runCode(myif *inst) {
std::cout << inst->myfunc(5) << std::endl;
}
目前我将研究在不将Python嵌入您的应用程序中的问题,即您在Python中启动异常,而不是在C ++的int main()
中启动。不过稍后添加这一点相当简单。
首先要做的是让跨语言多态正常工作:
%module(directors="1") module
%{
#include <iostream>
#include "myif.h"
%}
%feature("director") myif;
%include "myif.h"
为了实现这个功能,我们已经全局启用了 SWIG 的 director 特性,并针对我们的接口进行了特定设置。不过其余部分都是标准的 SWIG 内容。
我编写了一个测试 Python 实现:
import module
class MyCl(module.myif):
def __init__(self):
module.myif.__init__(self)
def myfunc(self,a):
return a*2.0
cl = MyCl()
print cl.myfunc(100.0)
module.runCode(cl)
有了这个,我能够编译并运行以下内容:
swig -python -c++ -Wall myif.i
g++ -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7
python mycl.py
200.0
10
这正是你希望从测试中看到的。
将Python嵌入应用程序:
接下来,我们需要实现一个真正的版本的mymain.cc。我已经草拟了一个大致的样子:
#include <iostream>
#include "myif.h"
#include <Python.h>
int main()
{
Py_Initialize();
const double input = 5.0;
PyObject *main = PyImport_AddModule("__main__");
PyObject *dict = PyModule_GetDict(main);
PySys_SetPath(".");
PyObject *module = PyImport_Import(PyString_FromString("mycl"));
PyModule_AddObject(main, "mycl", module);
PyObject *instance = PyRun_String("mycl.MyCl()", Py_eval_input, dict, dict);
PyObject *result = PyObject_CallMethod(instance, "myfunc", (char *)"(O)" ,PyFloat_FromDouble(input));
PyObject *error = PyErr_Occurred();
if (error) {
std::cerr << "Error occured in PyRun_String" << std::endl;
PyErr_Print();
}
double ret = PyFloat_AsDouble(result);
std::cout << ret << std::endl;
Py_Finalize();
return 0;
}
基本上它只是使用标准的Python嵌入其他应用程序。它可以正常工作,并提供了您所希望看到的结果:
g++ -Wall -Wextra -I/usr/include/python2.7 main.cc -o main -lpython2.7
./main
200.0
10
10
拼图的最后一块就是将从Python创建实例得到的PyObject*
转换为myif *
。 SWIG再次使这相当简单。
首先,我们需要要求SWIG向我们公开其运行时在头文件中的内容。我们可以通过额外调用SWIG来完成此操作:
swig -Wall -c++ -python -external-runtime runtime.h
接下来,我们需要重新编译SWIG模块,显式地给出SWIG知道的类型表的名称,以便我们可以在main.cc内部查找它。 我们使用以下命令重新编译.so文件:
g++ -DSWIG_TYPE_TABLE=myif -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7
然后,在我们的main.cc中添加一个帮助函数,用于将PyObject*
转换为myif*
:
#include "runtime.h"
myif *python2interface(PyObject *obj) {
void *argp1 = 0;
swig_type_info * pTypeInfo = SWIG_TypeQuery("myif *");
const int res = SWIG_ConvertPtr(obj, &argp1,pTypeInfo, 0);
if (!SWIG_IsOK(res)) {
abort();
}
return reinterpret_cast<myif*>(argp1);
}
现在我们已经完成这个步骤,可以从main()
中使用它了:
int main()
{
Py_Initialize();
const double input = 5.5;
PySys_SetPath(".");
PyObject *module = PyImport_ImportModule("mycl");
PyObject *cls = PyObject_GetAttrString(module, "MyCl");
PyObject *instance = PyObject_CallFunctionObjArgs(cls, NULL);
myif *inst = python2interface(instance);
std::cout << inst->myfunc(input) << std::endl;
Py_XDECREF(instance);
Py_XDECREF(cls);
Py_Finalize();
return 0;
}
最后,我们需要使用-DSWIG_TYPE_TABLE=myif
编译main.cc,得到如下结果:
./main
11