如何在Python中实现一个C ++类,由C ++调用? [英] How can I implement a C++ class in Python, to be called by C++?

查看:103
本文介绍了如何在Python中实现一个C ++类,由C ++调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用C ++编写的类接口。我有几个类实现这个接口也用C ++编写。这些在一个更大的C ++程序的上下文中被调用,它基本上实现了main。我想能够在Python中编写这个接口的实现,并允许它们在更大的C ++程序的上下文中使用,就好像它们是用C ++编写的一样。

I have a class interface written in C++. I have a few classes that implement this interface also written in C++. These are called in the context of a larger C++ program, which essentially implements "main". I want to be able to write implementations of this interface in Python, and allow them to be used in the context of the larger C++ program, as if they had been just written in C++.

有很多关于连接python和C ++的文章,但我不知道如何做我想要的。我可以找到的最近我在这里: http://www.cs.brown.edu/~jwicks/boost/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions ,但这不是'

There's been a lot written about interfacing python and C++ but I cannot quite figure out how to do what I want. The closest I can find is here: http://www.cs.brown.edu/~jwicks/boost/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions, but this isn't quite right.

更具体地说,假设我有一个现有的C ++接口,定义如下:

To be more concrete, suppose I have an existing C++ interface defined something like:

// myif.h
class myif {
   public:
     virtual float myfunc(float a);
};

我想要做的是:

// mycl.py
... some magic python stuff ...
class MyCl(myif):
  def myfunc(a):
    return a*2

然后,回到我的C ++代码想要能够说如下:

Then, back in my C++ code, I want to be able to say something like:

// mymain.cc
void main(...) {
  ... some magic c++ stuff ...
  myif c = MyCl();  // get the python class
  cout << c.myfunc(5) << endl;  // should print 10
}

我希望这已经足够清楚了) >

I hope this is sufficiently clear ;)

推荐答案

这个答案有两个部分。首先,你需要以一种允许Python实现覆盖它的一部分的方式在Python中公开你的接口。然后你需要显示你的C ++程序(在 main 如何调用Python。

There's two parts to this answer. First you need to expose your interface in Python in a way which allows Python implementations to override parts of it at will. Then you need to show your C++ program (in main how to call Python.

第一部分对SWIG非常容易,我稍微修改了你的示例场景以修复一些问题,添加了一个额外的测试功能:

The first part is pretty easy to do with SWIG. I modified your example scenario slightly to fix a few issues and added an extra function for testing:

// myif.h
class myif {
   public:
     virtual float myfunc(float a) = 0;
};

inline void runCode(myif *inst) {
  std::cout << inst->myfunc(5) << std::endl;
}

现在我将看看问题,而不是在你的应用程序中嵌入Python,也就是你在Python中开始excetion,而不是在C ++中的 int main()

For now I'll look at the problem without embedding Python in your application, i.e. you start excetion in Python, not in int main() in C++. It's fairly straightforward to add that later though.

首先是跨平台语言多态性

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

为了做到这一点,我们已经在全球范围内启用SWIG的导演功能,特别是我们的界面。

To do that we've enabled SWIG's director feature globally and specifically for our interface. The rest of it is pretty standard SWIG though.

我写了一个测试Python实现:

I wrote a test Python implementation:

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)

然后我就可以编译并运行: / p>

With that I was then able to compile and run this:


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

这个测试希望看到的内容。

Exactly what you'd hope to see from that test.

接下来我们需要实现一个真实版本的mymain.cc。我已经把它可能是什么样子的草图:

Next up we need to implement a real version of your mymain.cc. I've put together a sketch of what it might look like:

#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 。它的工作原理,并给出了你希望看到的:

It's basically just standard embedding Python in another application. It works and gives exactly what you'd hope to see also:


g++ -Wall -Wextra -I/usr/include/python2.7 main.cc -o main -lpython2.7
./main
200.0
10
10






最后一个难题是能够转换从创建实例获得的 PyObject * 在Python中转换成 myif * 。 SWIG再次使这一过程相当简单。


The final piece of the puzzle is being able to convert the PyObject* that you get from creating the instance in Python into a myif *. SWIG again makes this reasonably straightforward.

首先,我们需要让SWIG在我们的头文件中公开其运行时。我们这样做额外调用SWIG:

First we need to ask SWIG to expose its runtime in a headerfile for us. We do this with an extra call to SWIG:


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

接下来我们需要重新编译SWIG模块,明确给出SWIG类型的表格知道一个名称,这样我们可以从主.cc。我们使用以下命令重新编译.so:

Next we need to re-compile our SWIG module, explicitly giving the table of types SWIG knows about a name so we can look it up from within our main.cc. We recompile the .so using:


g++ -DSWIG_TYPE_TABLE=myif -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7

然后我们添加一个帮助函数来转换 PyObject

Then we add a helper function for converting the PyObject* to myif* in our main.cc:

#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);
}

现在这就是我们可以使用 main()

Now this is in place we can use it from within 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
11

这篇关于如何在Python中实现一个C ++类,由C ++调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆