2011-11-22 80 views
15

我正在寻找一种将C++类实例公开给python嵌入式解释器的简单方法。将C++类实例暴露给python嵌入式解释器

  • 我有一个C++库。该库被包裹(使用痛饮的时刻),我能够从Python解释器使用它
  • 我从我的图书馆instanciates一个Foo类和嵌入Python解释器一个C++主程序

我想将我的C++世界实例Foo展示给python世界(并且被看作是Foo类)。

这是可能的,如果是这样,怎么样?

我认为这几乎就像在第一个答案: boost::python::ptr or PyInstance_New usage

我想这意味着我应该使用boost.Python来包装我的图书馆?

我唯一的目标是在嵌入式Python解释器中操作我的C++ Foo实例(不确定它是否可以用前面的方法完成)。

希望我很清楚,谢谢你的帮助。

更新

谢谢您的回答。事实上,我已经将我的Foo类暴露给python(使用swig)。

我有什么:

我Foo类:

class Foo{...}; 

我的包裹库(包括Foo类)暴露在蟒蛇:这样我就可以启动Python解释器和做这样的事情:

import my_module 
foo=my_modulde.Foo() 

我想要什么:

有一个嵌入python解释器和操作C++世界变量的C++主程序。

int main(int argc, char **argv) 
{ 
    Foo foo; // instanciates foo 

    Py_Initialize(); 

    Py_Main(argc, argv); // starts the python interpreter 
         // and manipulates THE foo instance in it 

    Py_Finalize(); 

    return 0; 
} 

现在更清楚了吗? :)

回答

16

Boost python允许您以非常紧密集成的方式向C++公开C++类 - 甚至可以将它们包装起来,以便从C++类派生python类,并将虚方法解析为python覆盖。

boost python tutorial是一个很好的开始。


编辑:

您可以创建一个C++对象,并传递给内部Python解释这样的一个引用:

#include <boost/shared_ptr.hpp> 
#include <boost/make_shared.hpp> 
#include <boost/python.hpp> 
#include <string> 
#include <iostream> 

namespace bp = boost::python; 

struct Foo{ 
    Foo(){} 
    Foo(std::string const& s) : m_string(s){} 
    void doSomething() { 
     std::cout << "Foo:" << m_string << std::endl; 
    } 
    std::string m_string; 
}; 

typedef boost::shared_ptr<Foo> foo_ptr; 

BOOST_PYTHON_MODULE(hello) 
{ 
    bp::class_<Foo, foo_ptr>("Foo") 
     .def("doSomething", &Foo::doSomething) 
    ; 
}; 

int main(int argc, char **argv) 
{ 
    Py_Initialize(); 
    try { 
     PyRun_SimpleString(
      "a_foo = None\n" 
      "\n" 
      "def setup(a_foo_from_cxx):\n" 
      " print 'setup called with', a_foo_from_cxx\n" 
      " global a_foo\n" 
      " a_foo = a_foo_from_cxx\n" 
      "\n" 
      "def run():\n" 
      " a_foo.doSomething()\n" 
      "\n" 
      "print 'main module loaded'\n" 
     ); 

     foo_ptr a_cxx_foo = boost::make_shared<Foo>("c++"); 

     inithello(); 
     bp::object main = bp::object(bp::handle<>(bp::borrowed(
      PyImport_AddModule("__main__") 
     ))); 

     // pass the reference to a_cxx_foo into python: 
     bp::object setup_func = main.attr("setup"); 
     setup_func(a_cxx_foo); 

     // now run the python 'main' function 
     bp::object run_func = main.attr("run"); 
     run_func(); 
    } 
    catch (bp::error_already_set) { 
     PyErr_Print(); 
    } 

    Py_Finalize(); 

    return 0; 
} 
+0

感谢您的回答,我也会考虑boost.Python。我已经意识到boost.python公开C++类的功能,但我有很多遗留代码需要打包。所以swig看起来对我来说更好的解决方案(更快?更简单?以及更多的语言目标)...... :( 我已经找到了你的一个老答案[here](http://stackoverflow.com/q/3378195/1044695),但它仍然不明确:)但我觉得这正是我正在寻找的东西(不仅仅是一个例子,而是全球的想法在这里)? 我希望用swig和python api来做到这一点? :( – jineff

+0

我已经添加了一个例子,试图用boost :: python来回答你确切的问题 - 害怕我忍不住用swig。 – James

+0

非常感谢!这正是我想要的(除了boost.python)。已经提到你的答案是有用的(实际上非​​常有用:))。我会等待一些其他回复(特别是关于swig)并深入了解您的解决方案。 – jineff

0

在我看来,你可以做你的类的接口和从dll文件导出ti(使你的c + +项目库)。我不知道是否可以像Python中的接口一样管理com,但是您仍然可以导出c样式函数以便操纵Foo类的行为。

0

仅供参考,这里是你如何能实现这个使用pybind11

#include <pybind11/pybind11.h> 
#include <iostream> 

namespace py = pybind11; 

class Foo 
{ 
public: 
    Foo(const std::string &s) : s_(s) {} 
    void doSomething() { std::cout << s_ << std::endl; } 

private: 
    std::string s_; 
}; 

typedef std::shared_ptr<Foo> FooPtr; 

PYBIND11_PLUGIN(FooModule) 
{ 
    py::module m("FooModule"); 
    py::class_<Foo, FooPtr>(m, "Foo") 
     .def("doSomething", &Foo::doSomething); 
    return m.ptr(); 
} 

int main(int argc, char **argv) 
{ 
    // Create Foo instance in C++ 
    FooPtr foo = std::make_shared<Foo>("Hello, World!"); 

    // Initialize interpreter and import module defining Foo wrapper 
    PyImport_AppendInittab("FooModule", PyInit_FooModule); 
    Py_Initialize(); 
    PyRun_SimpleString("import FooModule"); 

    // Make Foo instance accessible in python 
    py::module main = py::module::import("__main__"); 
    main.attr("foo") = foo; 

    // Test that it works 
    PyRun_SimpleString("foo.doSomething()"); 

    // Finalize 
    Py_Finalize(); 
    return 0; 
}