如何在Python中实现c++类,由c++调用?

时间:2022-11-28 09:54:17

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++.

我有一个用c++编写的类接口。我有一些实现这个接口的类,它们也是用c++编写的。这些调用是在大型c++程序的上下文中调用的,该程序实际上实现了“main”。我希望能够用Python编写这个接口的实现,并允许它们在更大的c++程序上下文中使用,就好像它们是用c++编写的一样。

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.

已经有很多关于接口python和c++的文章了,但是我不知道如何做我想做的事情。我能找到的最接近的是:http://www.cs.brown.edu/~jwicks/boost/libs/python/doc/教程/doc/ doc/html/python/exposing.html#python.class_virtual_functions,但这并不完全正确。

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

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

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

What I want to be able to do is something like:

我想做的是:

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

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

然后,回到我的c++代码中,我想说:

// 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 ;)

我希望这是足够清楚的;

6 个解决方案

#1


39  

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.

这个答案有两部分。首先,需要用Python公开接口,以便Python实现可以随意覆盖接口的部分。然后需要展示c++程序(主要是如何调用Python)。


Exposing the existing interface to Python:

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:

第一部分和SWIG很容易。我稍微修改了您的示例场景,以修复一些问题,并添加了一个用于测试的额外功能:

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

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

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.

现在,我将在不将Python嵌入应用程序的情况下研究这个问题,也就是说,您开始使用Python而不是使用c++的int main()。稍后再添加这个是很简单的。

First up is getting cross-language polymorphism working:

首先是使跨语言多态性工作:

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

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.

为此,我们启用了SWIG的director特性,特别是我们的界面。其余的都是标准的饮料。

I wrote a test Python implementation:

我编写了一个测试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)

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.

正是你希望从测试中看到的。


Embedding the Python in the application:

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:

接下来,我们需要实现一个真正的my .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;
}

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

它基本上只是在另一个应用程序中嵌入Python的标准。它起作用了,并提供了你希望看到的东西:

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

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.

最后一个难题是能够将在Python中创建实例得到的PyObject*转换为myif *。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在头文件中公开它的运行时。我们要做的是给SWIG一个额外的电话:

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

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:

接下来,我们需要重新编译我们的SWIG模块,显式地给出SWIG类型的表,这样我们就可以从main.cc中查找它。我们重新编译。使用:

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

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

然后我们添加了一个助手函数,用于将PyObject*转换为main.cc中的myif*:

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

Now this is in place we can use it from within main():

现在我们可以在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;
}

Finally we have to compile main.cc with -DSWIG_TYPE_TABLE=myif and this gives:

最后我们要编译main。cc与-DSWIG_TYPE_TABLE=myif,得到:

./main
11

#2


12  

Minimal example; note that it is complicated by the fact that Base is not pure virtual. There we go:

最小的例子;注意,基础不是纯虚拟的,这是复杂的。好了:

  1. baz.cpp:

    baz.cpp:

    #include<string>
    #include<boost/python.hpp>
    using std::string;
    namespace py=boost::python;
    
    struct Base{
      virtual string foo() const { return "Base.foo"; }
      // fooBase is non-virtual, calling it from anywhere (c++ or python)
      // will go through c++ dispatch
      string fooBase() const { return foo(); }
    };
    struct BaseWrapper: Base, py::wrapper<Base>{
      string foo() const{
        // if Base were abstract (non-instantiable in python), then
        // there would be only this->get_override("foo")() here
        //
        // if called on a class which overrides foo in python
        if(this->get_override("foo")) return this->get_override("foo")();
        // no override in python; happens if Base(Wrapper) is instantiated directly
        else return Base::foo();
      }
    };
    
    BOOST_PYTHON_MODULE(baz){
      py::class_<BaseWrapper,boost::noncopyable>("Base")
        .def("foo",&Base::foo)
        .def("fooBase",&Base::fooBase)
      ;
    }
    
  2. bar.py

    bar.py

    import sys
    sys.path.append('.')
    import baz
    
    class PyDerived(baz.Base):
      def foo(self): return 'PyDerived.foo'
    
    base=baz.Base()
    der=PyDerived()
    print base.foo(), base.fooBase()
    print der.foo(), der.fooBase()
    
  3. Makefile

    Makefile

    default:
           g++ -shared -fPIC -o baz.so baz.cpp -lboost_python `pkg-config python --cflags`
    

And the result is:

结果是:

Base.foo Base.foo
PyDerived.foo PyDerived.foo

where you can see how fooBase() (the non-virtual c++ function) calls virtual foo(), which resolves to the override regardless whether in c++ or python. You could derive a class from Base in c++ and it would work just the same.

您可以看到fooBase()(非虚c++函数)如何调用virtual foo(),无论在c++还是python中,它都解析为覆盖。您可以从c++中的Base派生类,它的工作原理是一样的。

EDIT (extracting c++ object):

编辑(提取c++对象):

PyObject* obj; // given
py::object pyObj(obj); // wrap as boost::python object (cheap)
py::extract<Base> ex(pyObj); 
if(ex.check()){ // types are compatible
  Base& b=ex(); // get the wrapped object
  // ...
} else {
  // error
}

// shorter, thrwos when conversion not possible
Base &b=py::extract<Base>(py::object(obj))();

Construct py::object from PyObject* and use py::extract to query whether the python object matches what you are trying to extract: PyObject* obj; py::extract<Base> extractor(py::object(obj)); if(!extractor.check()) /* error */; Base& b=extractor();

构造PyObject*中的py::对象,并使用py::extract查询python对象是否匹配要提取的:PyObject* obj;py:: <基础> 提取器(py:对象(obj));如果(! extractor.check())/ *错误* /;基地b =器();

#3


10  

Quoting http://wiki.python.org/moin/boost.python/Inheritance

引用http://wiki.python.org/moin/boost.python/Inheritance

"Boost.Python also allows us to represent C++ inheritance relationships so that wrapped derived classes may be passed where values, pointers, or references to a base class are expected as arguments."

“提振。Python还允许我们表示c++继承关系,以便在需要作为参数的基类的值、指针或引用时传递封装的派生类。

There are examples of virtual functions so that solves the first part (the one with class MyCl(myif))

有一些虚函数的例子可以解决第一部分(MyCl(myif)类)

For specific examples doing this, http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions

具体示例如下:http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions。

For the line myif c = MyCl(); you need to expose your python (module) to C++. There are examples here http://wiki.python.org/moin/boost.python/EmbeddingPython

对于myif myc = MyCl()行;您需要将python(模块)公开给c++。这里有一些例子http://wiki.python.org/moin/boost. python/embed ingpython

#4


8  

Based upon the (very helpful) answer by Eudoxos I've taken his code and extended it such that there is now an embedded interpreter, with a built-in module.

基于Eudoxos(非常有用)的回答,我使用了他的代码并对其进行了扩展,现在有了嵌入式解释器和内置模块。

This answer is the Boost.Python equivalent of my SWIG based answer.

这个答案就是推动力。Python相当于我基于SWIG的答案。

The headerfile myif.h:

的headerfile myif.h:

class myif {
public:
  virtual float myfunc(float a) const { return 0; }
  virtual ~myif() {}
};

Is basically as in the question, but with a default implementation of myfunc and a virtual destructor.

基本上和问题一样,但是有myfunc的默认实现和一个虚拟析构函数。

For the Python implementation, MyCl.py I have basically the same as the question:

对于Python实现,MyCl。我的问题基本上是一样的:

import myif

class MyCl(myif.myif):
  def myfunc(self,a): 
    return a*2.0

This then leaves mymain.cc, most of which is based upon the answer from Eudoxos:

这叶子mymain。抄送,大部分是基于Eudoxos的回答:

#include <boost/python.hpp>
#include <iostream>
#include "myif.h"

using namespace boost::python;

// This is basically Eudoxos's answer:
struct MyIfWrapper: myif, wrapper<myif>{
  float myfunc(float a) const {
    if(this->get_override("myfunc")) 
      return this->get_override("myfunc")(a);
    else 
      return myif::myfunc(a);
  }
};

BOOST_PYTHON_MODULE(myif){
  class_<MyIfWrapper,boost::noncopyable>("myif")
    .def("myfunc",&myif::myfunc)
  ;
}
// End answer by Eudoxos

int main( int argc, char ** argv ) {
  try {
    // Tell python that "myif" is a built-in module
    PyImport_AppendInittab("myif", initmyif);
    // Set up embedded Python interpreter:
    Py_Initialize();

    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");

    PySys_SetPath(".");
    main_namespace["mycl"] = import("mycl");

    // Create the Python object with an eval()
    object obj = eval("mycl.MyCl()", main_namespace);

    // Find the base C++ type for the Python object (from Eudoxos)
    const myif &b=extract<myif>(obj)();
    std::cout << b.myfunc(5) << std::endl;

  } catch( error_already_set ) {
    PyErr_Print();
  }
}

The key part that I've added here, above and beyond the "how do I embed Python using Boost.Python?" and "how do I extend Python using Boost.python?" (which was answered by Eudoxos) is the answer to the question "How do I do both at once in the same program?". The solution to this lies with the PyImport_AppendInittab call, which takes the initialisation function that would normally be called when the module is loaded and registers it as a built-in module. Thus when mycl.py says import myif it ends up importing the built-in Boost.Python module.

除了“如何使用boot .Python嵌入Python”和“如何使用boot .Python扩展Python?”(Eudoxos回答了这个问题)是“我如何在同一个程序中同时做这两件事?”解决这个问题的方法是使用PyImport_AppendInittab调用,该调用采用初始化函数,通常在加载模块时调用该函数并将其注册为内置模块。因此,当mycl。py表示,导入myif最终会导入内置的Boost。Python模块。

#5


1  

Take a look at Boost Python, that is the most versatile and powerful tool to bridge between C++ and Python.

让我们来看看Boost Python,它是连接c++和Python的最通用和最强大的工具。

http://www.boost.org/doc/libs/1_48_0/libs/python/doc/

http://www.boost.org/doc/libs/1_48_0/libs/python/doc/

#6


-2  

There's no real way to interface C++ code directly with Python.

没有真正的方法可以直接与Python接口c++代码。

SWIG does handle this, but it builds its own wrapper.

SWIG确实处理了这个问题,但它构建了自己的包装器。

One alternative I prefer over SWIG is ctypes, but to use this you need to create a C wrapper.

与SWIG相比,我更喜欢的一种选择是ctype,但是要使用它,需要创建一个C包装器。

For the example:

的例子:

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

Build a C wrapper like so:

像这样构建一个C包装器:

extern "C" __declspec(dllexport) float myif_myfunc(myif* m, float a) {
    return m->myfunc(a);
}

Since you are building using C++, the extern "C" allows for C linkage so you can call it easily from your dll, and __declspec(dllexport) allows the function to be called from the dll.

由于您正在使用c++构建,所以extern“C”允许使用C链接,因此您可以轻松地从dll调用它,而__declspec(dllexport)允许从dll调用函数。

In Python:

在Python中:

from ctypes import *
from os.path import dirname

dlldir = dirname(__file__)                      # this strips it to the directory only
dlldir.replace( '\\', '\\\\' )                  # Replaces \ with \\ in dlldir
lib = cdll.LoadLibrary(dlldir+'\\myif.dll')     # Loads from the full path to your module.

# Just an alias for the void pointer for your class
c_myif = c_void_p

# This tells Python how to interpret the return type and arguments
lib.myif_myfunc.argtypes = [ c_myif, c_float ]
lib.myif_myfunc.restype  = c_float

class MyCl(myif):
    def __init__:
        # Assume you wrapped a constructor for myif in C
        self.obj = lib.myif_newmyif(None)

    def myfunc(a):
        return lib.myif_myfunc(self.obj, a)

While SWIG does all this for you, there's little room for you to modify things as you please without getting frustrated at all the changes you have to redo when you regenerate the SWIG wrapper.

当SWIG为您完成所有这些工作时,您几乎没有空间可以随心所欲地修改内容,而不会对重新生成SWIG包装器时必须进行的所有更改感到沮丧。

One issue with ctypes is that it doesn't handle STL structures, since it's made for C. SWIG does handle this for you, but you may be able to wrap it yourself in the C. It's up to you.

ctypes的一个问题是它不能处理STL结构,因为它是C. SWIG为您处理的,但是您可以自己将它封装在c中。

Here's the Python doc for ctypes:

以下是针对ctype的Python文档:

http://docs.python.org/library/ctypes.html

http://docs.python.org/library/ctypes.html

Also, the built dll should be in the same folder as your Python interface (why wouldn't it be?).

此外,所构建的dll应该与Python接口位于同一个文件夹中(为什么不是呢?)

I am curious though, why would you want to call Python from inside C++ instead of calling the C++ implementation directly?

我很好奇,为什么您要从c++内部调用Python而不是直接调用c++实现呢?

#1


39  

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.

这个答案有两部分。首先,需要用Python公开接口,以便Python实现可以随意覆盖接口的部分。然后需要展示c++程序(主要是如何调用Python)。


Exposing the existing interface to Python:

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:

第一部分和SWIG很容易。我稍微修改了您的示例场景,以修复一些问题,并添加了一个用于测试的额外功能:

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

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

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.

现在,我将在不将Python嵌入应用程序的情况下研究这个问题,也就是说,您开始使用Python而不是使用c++的int main()。稍后再添加这个是很简单的。

First up is getting cross-language polymorphism working:

首先是使跨语言多态性工作:

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

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.

为此,我们启用了SWIG的director特性,特别是我们的界面。其余的都是标准的饮料。

I wrote a test Python implementation:

我编写了一个测试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)

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.

正是你希望从测试中看到的。


Embedding the Python in the application:

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:

接下来,我们需要实现一个真正的my .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;
}

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

它基本上只是在另一个应用程序中嵌入Python的标准。它起作用了,并提供了你希望看到的东西:

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

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.

最后一个难题是能够将在Python中创建实例得到的PyObject*转换为myif *。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在头文件中公开它的运行时。我们要做的是给SWIG一个额外的电话:

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

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:

接下来,我们需要重新编译我们的SWIG模块,显式地给出SWIG类型的表,这样我们就可以从main.cc中查找它。我们重新编译。使用:

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

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

然后我们添加了一个助手函数,用于将PyObject*转换为main.cc中的myif*:

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

Now this is in place we can use it from within main():

现在我们可以在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;
}

Finally we have to compile main.cc with -DSWIG_TYPE_TABLE=myif and this gives:

最后我们要编译main。cc与-DSWIG_TYPE_TABLE=myif,得到:

./main
11

#2


12  

Minimal example; note that it is complicated by the fact that Base is not pure virtual. There we go:

最小的例子;注意,基础不是纯虚拟的,这是复杂的。好了:

  1. baz.cpp:

    baz.cpp:

    #include<string>
    #include<boost/python.hpp>
    using std::string;
    namespace py=boost::python;
    
    struct Base{
      virtual string foo() const { return "Base.foo"; }
      // fooBase is non-virtual, calling it from anywhere (c++ or python)
      // will go through c++ dispatch
      string fooBase() const { return foo(); }
    };
    struct BaseWrapper: Base, py::wrapper<Base>{
      string foo() const{
        // if Base were abstract (non-instantiable in python), then
        // there would be only this->get_override("foo")() here
        //
        // if called on a class which overrides foo in python
        if(this->get_override("foo")) return this->get_override("foo")();
        // no override in python; happens if Base(Wrapper) is instantiated directly
        else return Base::foo();
      }
    };
    
    BOOST_PYTHON_MODULE(baz){
      py::class_<BaseWrapper,boost::noncopyable>("Base")
        .def("foo",&Base::foo)
        .def("fooBase",&Base::fooBase)
      ;
    }
    
  2. bar.py

    bar.py

    import sys
    sys.path.append('.')
    import baz
    
    class PyDerived(baz.Base):
      def foo(self): return 'PyDerived.foo'
    
    base=baz.Base()
    der=PyDerived()
    print base.foo(), base.fooBase()
    print der.foo(), der.fooBase()
    
  3. Makefile

    Makefile

    default:
           g++ -shared -fPIC -o baz.so baz.cpp -lboost_python `pkg-config python --cflags`
    

And the result is:

结果是:

Base.foo Base.foo
PyDerived.foo PyDerived.foo

where you can see how fooBase() (the non-virtual c++ function) calls virtual foo(), which resolves to the override regardless whether in c++ or python. You could derive a class from Base in c++ and it would work just the same.

您可以看到fooBase()(非虚c++函数)如何调用virtual foo(),无论在c++还是python中,它都解析为覆盖。您可以从c++中的Base派生类,它的工作原理是一样的。

EDIT (extracting c++ object):

编辑(提取c++对象):

PyObject* obj; // given
py::object pyObj(obj); // wrap as boost::python object (cheap)
py::extract<Base> ex(pyObj); 
if(ex.check()){ // types are compatible
  Base& b=ex(); // get the wrapped object
  // ...
} else {
  // error
}

// shorter, thrwos when conversion not possible
Base &b=py::extract<Base>(py::object(obj))();

Construct py::object from PyObject* and use py::extract to query whether the python object matches what you are trying to extract: PyObject* obj; py::extract<Base> extractor(py::object(obj)); if(!extractor.check()) /* error */; Base& b=extractor();

构造PyObject*中的py::对象,并使用py::extract查询python对象是否匹配要提取的:PyObject* obj;py:: <基础> 提取器(py:对象(obj));如果(! extractor.check())/ *错误* /;基地b =器();

#3


10  

Quoting http://wiki.python.org/moin/boost.python/Inheritance

引用http://wiki.python.org/moin/boost.python/Inheritance

"Boost.Python also allows us to represent C++ inheritance relationships so that wrapped derived classes may be passed where values, pointers, or references to a base class are expected as arguments."

“提振。Python还允许我们表示c++继承关系,以便在需要作为参数的基类的值、指针或引用时传递封装的派生类。

There are examples of virtual functions so that solves the first part (the one with class MyCl(myif))

有一些虚函数的例子可以解决第一部分(MyCl(myif)类)

For specific examples doing this, http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions

具体示例如下:http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions。

For the line myif c = MyCl(); you need to expose your python (module) to C++. There are examples here http://wiki.python.org/moin/boost.python/EmbeddingPython

对于myif myc = MyCl()行;您需要将python(模块)公开给c++。这里有一些例子http://wiki.python.org/moin/boost. python/embed ingpython

#4


8  

Based upon the (very helpful) answer by Eudoxos I've taken his code and extended it such that there is now an embedded interpreter, with a built-in module.

基于Eudoxos(非常有用)的回答,我使用了他的代码并对其进行了扩展,现在有了嵌入式解释器和内置模块。

This answer is the Boost.Python equivalent of my SWIG based answer.

这个答案就是推动力。Python相当于我基于SWIG的答案。

The headerfile myif.h:

的headerfile myif.h:

class myif {
public:
  virtual float myfunc(float a) const { return 0; }
  virtual ~myif() {}
};

Is basically as in the question, but with a default implementation of myfunc and a virtual destructor.

基本上和问题一样,但是有myfunc的默认实现和一个虚拟析构函数。

For the Python implementation, MyCl.py I have basically the same as the question:

对于Python实现,MyCl。我的问题基本上是一样的:

import myif

class MyCl(myif.myif):
  def myfunc(self,a): 
    return a*2.0

This then leaves mymain.cc, most of which is based upon the answer from Eudoxos:

这叶子mymain。抄送,大部分是基于Eudoxos的回答:

#include <boost/python.hpp>
#include <iostream>
#include "myif.h"

using namespace boost::python;

// This is basically Eudoxos's answer:
struct MyIfWrapper: myif, wrapper<myif>{
  float myfunc(float a) const {
    if(this->get_override("myfunc")) 
      return this->get_override("myfunc")(a);
    else 
      return myif::myfunc(a);
  }
};

BOOST_PYTHON_MODULE(myif){
  class_<MyIfWrapper,boost::noncopyable>("myif")
    .def("myfunc",&myif::myfunc)
  ;
}
// End answer by Eudoxos

int main( int argc, char ** argv ) {
  try {
    // Tell python that "myif" is a built-in module
    PyImport_AppendInittab("myif", initmyif);
    // Set up embedded Python interpreter:
    Py_Initialize();

    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");

    PySys_SetPath(".");
    main_namespace["mycl"] = import("mycl");

    // Create the Python object with an eval()
    object obj = eval("mycl.MyCl()", main_namespace);

    // Find the base C++ type for the Python object (from Eudoxos)
    const myif &b=extract<myif>(obj)();
    std::cout << b.myfunc(5) << std::endl;

  } catch( error_already_set ) {
    PyErr_Print();
  }
}

The key part that I've added here, above and beyond the "how do I embed Python using Boost.Python?" and "how do I extend Python using Boost.python?" (which was answered by Eudoxos) is the answer to the question "How do I do both at once in the same program?". The solution to this lies with the PyImport_AppendInittab call, which takes the initialisation function that would normally be called when the module is loaded and registers it as a built-in module. Thus when mycl.py says import myif it ends up importing the built-in Boost.Python module.

除了“如何使用boot .Python嵌入Python”和“如何使用boot .Python扩展Python?”(Eudoxos回答了这个问题)是“我如何在同一个程序中同时做这两件事?”解决这个问题的方法是使用PyImport_AppendInittab调用,该调用采用初始化函数,通常在加载模块时调用该函数并将其注册为内置模块。因此,当mycl。py表示,导入myif最终会导入内置的Boost。Python模块。

#5


1  

Take a look at Boost Python, that is the most versatile and powerful tool to bridge between C++ and Python.

让我们来看看Boost Python,它是连接c++和Python的最通用和最强大的工具。

http://www.boost.org/doc/libs/1_48_0/libs/python/doc/

http://www.boost.org/doc/libs/1_48_0/libs/python/doc/

#6


-2  

There's no real way to interface C++ code directly with Python.

没有真正的方法可以直接与Python接口c++代码。

SWIG does handle this, but it builds its own wrapper.

SWIG确实处理了这个问题,但它构建了自己的包装器。

One alternative I prefer over SWIG is ctypes, but to use this you need to create a C wrapper.

与SWIG相比,我更喜欢的一种选择是ctype,但是要使用它,需要创建一个C包装器。

For the example:

的例子:

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

Build a C wrapper like so:

像这样构建一个C包装器:

extern "C" __declspec(dllexport) float myif_myfunc(myif* m, float a) {
    return m->myfunc(a);
}

Since you are building using C++, the extern "C" allows for C linkage so you can call it easily from your dll, and __declspec(dllexport) allows the function to be called from the dll.

由于您正在使用c++构建,所以extern“C”允许使用C链接,因此您可以轻松地从dll调用它,而__declspec(dllexport)允许从dll调用函数。

In Python:

在Python中:

from ctypes import *
from os.path import dirname

dlldir = dirname(__file__)                      # this strips it to the directory only
dlldir.replace( '\\', '\\\\' )                  # Replaces \ with \\ in dlldir
lib = cdll.LoadLibrary(dlldir+'\\myif.dll')     # Loads from the full path to your module.

# Just an alias for the void pointer for your class
c_myif = c_void_p

# This tells Python how to interpret the return type and arguments
lib.myif_myfunc.argtypes = [ c_myif, c_float ]
lib.myif_myfunc.restype  = c_float

class MyCl(myif):
    def __init__:
        # Assume you wrapped a constructor for myif in C
        self.obj = lib.myif_newmyif(None)

    def myfunc(a):
        return lib.myif_myfunc(self.obj, a)

While SWIG does all this for you, there's little room for you to modify things as you please without getting frustrated at all the changes you have to redo when you regenerate the SWIG wrapper.

当SWIG为您完成所有这些工作时,您几乎没有空间可以随心所欲地修改内容,而不会对重新生成SWIG包装器时必须进行的所有更改感到沮丧。

One issue with ctypes is that it doesn't handle STL structures, since it's made for C. SWIG does handle this for you, but you may be able to wrap it yourself in the C. It's up to you.

ctypes的一个问题是它不能处理STL结构,因为它是C. SWIG为您处理的,但是您可以自己将它封装在c中。

Here's the Python doc for ctypes:

以下是针对ctype的Python文档:

http://docs.python.org/library/ctypes.html

http://docs.python.org/library/ctypes.html

Also, the built dll should be in the same folder as your Python interface (why wouldn't it be?).

此外,所构建的dll应该与Python接口位于同一个文件夹中(为什么不是呢?)

I am curious though, why would you want to call Python from inside C++ instead of calling the C++ implementation directly?

我很好奇,为什么您要从c++内部调用Python而不是直接调用c++实现呢?