2011-11-22 69 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; 
}