<p>这个答案有两部分。首先,您需要以一种允许Python实现随意重写部分内容的方式在Python中公开接口。然后,您需要显示C++程序(在{<CD1> }如何调用Python。</p>
<hr/>
<h2>向Python公开现有接口:</h2>
<p>第一部分用SWIG很容易。我稍微修改了示例场景以解决一些问题,并添加了一个额外的测试功能:</p>
<pre><code>// myif.h
class myif {
public:
virtual float myfunc(float a) = 0;
};
inline void runCode(myif *inst) {
std::cout << inst->myfunc(5) << std::endl;
}
</code></pre>
<P>现在我将在不在应用程序中嵌入Python的情况下查看这个问题,即在Python中开始使用,而不是在C++中使用{{CD2>}。不过,以后再加上这一点是相当简单的。</p>
<p>首先是得到<a href="http://www.swig.org/Doc2.0/SWIGDocumentation.html#Python_directors">cross-language polymorphism working</a>:</p>
<pre><code>%module(directors="1") module
// We need to include myif.h in the SWIG generated C++ file
%{
#include <iostream>
#include "myif.h"
%}
// Enable cross-language polymorphism in the SWIG wrapper.
// It's pretty slow so not enable by default
%feature("director") myif;
// Tell swig to wrap everything in myif.h
%include "myif.h"
</code></pre>
<p>为了做到这一点,我们在全球范围内启用了SWIG的director功能,特别是针对我们的界面。不过,其余的都是相当标准的饮料。</p>
<p>我编写了一个测试Python实现:</p>
<pre><code>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)
</code></pre>
<p>然后我就可以编译并运行这个:</p>
<pre>
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
</pre>
<p>正是你希望从测试中看到的。</p>
<hr/>
<h2>在应用程序中嵌入Python:</h2>
<p>接下来我们需要实现mymain.cc的真实版本。我已经画了一幅草图:</p>
<pre><code>#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;
}
</code></pre>
<p>基本上只是标准的<a href="http://docs.python.org/extending/embedding.html">embedding Python in another application</a>。它的工作原理和给出的正是你希望看到的:</p>
<pre>
g++ -Wall -Wextra -I/usr/include/python2.7 main.cc -o main -lpython2.7
./main
200.0
10
10
</pre>
<hr/>
<p>最后一个难题是能够将在Python中创建实例得到的<code>PyObject*</code>转换为<code>myif *</code>。SWIG再一次让这变得相当简单。</p>
<p>首先,我们需要让SWIG在headerfile中为我们公开它的运行时。我们需要给SWIG打一个额外的电话:</p>
<pre>
swig -Wall -c++ -python -external-runtime runtime.h
</pre>
<p>接下来,我们需要重新编译SWIG模块,显式地给出SWIG知道名称的类型表,以便我们可以在main.cc中查找它。我们重新编译。因此使用:</p>
<pre>
g++ -DSWIG_TYPE_TABLE=myif -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7
</pre>
<p>然后,我们添加一个helper函数,用于将main.cc中的<code>PyObject*</code>转换为<code>myif*</code>:</p>
<pre><code>#include "runtime.h"
// runtime.h was generated by SWIG for us with the second call we made
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);
}
</code></pre>
<p>现在我们可以在<code>main()</code>中使用它了:</p>
<pre><code>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;
}
</code></pre>
<p>最后,我们必须用<code>-DSWIG_TYPE_TABLE=myif</code>编译main.cc,这给出了:</p>
<pre>
./main
11
</pre>