打通Python和C++之后?你懂的!
Python作為世界上***的 膠水 語言(哼,世界上***的語言當然是PHP==),利用Python的簡潔和C++的高效,基本可以解決99%的問題了吧~
一般的,Python和C++的交互分為這兩種情況:
- 用C++擴展Python:當一個Python項目中出現(xiàn)了性能瓶頸時,將瓶頸部分抽離出來,用C++封裝成一個Python可以調(diào)用的模塊(so庫);
- 將Python內(nèi)嵌入C++:當一個C++項目中有部分功能預期將會經(jīng)常變更需求,期望獲得更高的靈活性時,將這部分功能用Python實現(xiàn),在C++中進行調(diào)用。這篇文章將簡單介紹下***部分的一種做法。
Boost.Python
Boost作為一個大寶庫,提供了我們所需要的這一功能。并且,在Boost的許多庫中,已經(jīng)默認使用了Boost.Python,所以也算是經(jīng)過了充分的測試。
安裝
Boost的大部分功能都是以頭文件的形式提供的,無需安裝;但是也有少部分功能,需要進行手動編譯。不幸,Boost.Python也是其中之一。
參照 Getting Started on Unix Variants 的第五部分內(nèi)容,即可安裝Boost.Python。安裝完成后,可以在相關(guān)目錄(我的是/usr/local/lib下)看到相關(guān)的so文件。
Hello World
用C++實現(xiàn)一個模塊,在Python中調(diào)用時,可以返回一個特定的字符串。
++
- #include <boost/python.hpp>
- char const* greet()
- {
- return "hello, boost";
- }
- BOOST_PYTHON_MODULE(hello_boostpy)
- {
- using namespace boost::python;
- def("greet", greet);
- }
太簡單了,代碼基本說明了一切~
將其編譯成動態(tài)鏈接庫的形式:
- g++ -I /usr/include/python2.7/ -fPIC -shared -o hello_boostpy.so hello_boostpy.cc -lboost_python
這時可以使用ldd看看hello_boostpy.so可不可以找到libboost_python,找不到的話,需要手動將其路徑加入環(huán)境變量LD_LIBRARY_PATH中,或者用ldconfig相關(guān)的命令也可以。
接下來就可以在Python中使用hello_boostpy庫了:
- # -*- coding: utf-8 -*-
- import sys
- sys.path.append('.')
- def test():
- import hello_boostpy
- return hello_boostpy.greet()
- if __name__ == "__main__":
- print test()
Expose Class
接下來,我們在C++實現(xiàn)的模塊中,添加一個類,并且嘗試向C++方向傳入Python的list類型對象。
C++類:
++
- #include <boost/python.hpp>
- #include <vector>
- #include <string>
- #include <sstream>
- using namespace boost::python;
- struct Person
- {
- void set_name(std::string name) { this->name = name; }
- std::string print_info();
- void set_items(list& prices, list& discounts);
- std::string name;
- std::vector<double> item_prices;
- std::vector<double> item_discounts;
- };
其中,Python方的list類型,在Boost.Python中有一個對應(yīng)的實現(xiàn)boost::python::list(相應(yīng)的,dict、tuple等類型都有對應(yīng)實現(xiàn))。在set_items中,我們將會用boost::python::extract對數(shù)據(jù)類型做一個轉(zhuǎn)換。
++
- void Person::set_items(list& prices, list& discounts)
- {
- for(int i = 0; i < len(prices); ++i)
- {
- double price = extract<double>(prices[i]);
- double discount = extract<double>(discounts[i]);
- item_prices.push_back(price);
- item_discounts.push_back(discount);
- }
- }
Python模塊定義部分依舊是非常直觀的代碼:
- BOOST_PYTHON_MODULE(person)
- {
- class_<Person>("Person")
- .def("set_name", &Person::set_name)
- .def("print_info", &Person::print_info)
- .def("set_items", &Person::set_items)
- ;
- }
在Python代碼中,就可以像使用一個Python定義的類一樣使用Person類了:
- # -*- coding: utf-8 -*-
- import sys
- sys.path.append('.')
- def test():
- import person
- p = person.Person()
- p.set_name('Qie')
- p.set_items([100, 123.456, 888.8], [0.3, 0.1, 0.5])
- print p.print_info()
- if __name__ == "__main__":
- test()
Py++
上面的模塊封裝過程,看上去還是有些枯燥,有不少地方都是重復的工作。那么可不可以自動的進行呢?Py++提供了這樣的能力,它可以幫你自動生成Boost.Python的相關(guān)代碼,對于接口數(shù)量比較多的模塊來說,可以極大的減少工作量,也減少了出錯的概率。具體使用方法,可以參見 Tutorial