bosst-python使用 混合編程

1.為什么要混合編程?

python的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):編寫簡單

缺點(diǎn):Python的運(yùn)行速度是很慢的

注意:

如果只是IO耗時,換成C++程序意義也不大

2.常用的混合編程方法

  1. ctypes

    ctypes是python的標(biāo)準(zhǔn)庫,可以直接調(diào)用c/c++編寫的動態(tài)鏈接庫

    但是C++程序員需要以C語言的調(diào)用約定來提供接口,沒有類,沒有重載函數(shù),沒有模板,沒有C++異常

  2. Cython語言(python)

    一種類似于Python語言的一種新型語言編寫預(yù)定功能的代碼,然后將這些代碼轉(zhuǎn)換成為C語言編譯成為Python語言可以直接調(diào)用的二進(jìn)制模塊。

  3. boost.python

    與ctypes/Cython形成鮮明的對比,boost.python傾向于讓C++程序員擁有更熟悉的編程環(huán)境。

  4. SWIG或SIP(c語言數(shù)據(jù)類型 映射文件)

    通過編寫一個接口文件,使用類似于C/C++語法——聲明函數(shù)、類型的信息,然后使用特殊的工具為C/C++的代碼生成Python的接口代碼

3.boost::python環(huán)境搭建

  1. 安裝和配置boost

    a.安裝boost

    sudo apt-get install libboost-all-dev    
    

    b.創(chuàng)建libboost_python3.so的軟連接

    查找so庫的位置
    sudo find / -name "libboost_python-py35.so"
    進(jìn)入庫的路徑
    cd /usr/lib/x86_64-linux-gnu/
    創(chuàng)建軟連接
    sudo ln -s libboost_python-py35.so libboost_python3.so
    
  2. 如果出現(xiàn)如下問題:

fatal error: pyconfig.h: No such file or directory

解決辦法:

a.找到pyconfig.h文件位置

locate pyconfig.h

b.添加路徑到環(huán)境變量

編輯.bashrc添加如下內(nèi)容(路徑為查找到的路徑)

export CPLUS_INCLUDE_PATH=/usr/include/python3.4m

也可以配置anconda中的
export CPLUS_INCLUDE_PATH=/home/cai/anaconda3/pkgs/python-3.6.2-0/include/python3.6m
這個配置的是頭文件,我想c++的東西在環(huán)境變量中估計(jì)都是頭文件

python版本含義:

--with-pydebug (flag: d)
--with-pymalloc (flag: m)
--with-wide-unicode (flag: u)

4.helloworld

1.代碼

// 必須包含這個頭文件
#include <boost/python.hpp>
// 使用boost:python的命名空間
using namespace boost::python;

//定義的函數(shù)
char const* greet()
{
    return "hello, world";
}

//宏對需要導(dǎo)出的函數(shù)、全局變量、類等導(dǎo)入Python的Module_Name模塊
BOOST_PYTHON_MODULE(hello)//模塊名
{
    //第一個:對應(yīng)的python函數(shù)名
    //第二個:需要導(dǎo)出的c++函數(shù)名
    def("greet", greet);
}
  1. 生成python模塊so
g++ -shared -o hello.so -fPIC -I /usr/include/python3.4m/ main.cpp -lpython3.4m -lboost_python3

參數(shù)含義:

-shared    // 指定生成動態(tài)鏈接庫
-o    // 生成的動態(tài)鏈接庫的名稱
-fPIC    // 表示使用地址無關(guān)代碼
-I(大寫的i)    // 表示將/usr/include/python3.4m/目錄作為第一個尋找頭文件的目錄(主要查找pyconfig.h,如果配置了環(huán)境變量可以不指定)
-l    // 指定需鏈接的庫名

4.通過clion生成python模塊

配置CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(BoostPython)

set(CMAKE_CXX_STANDARD 14)

add_definitions(-fPIC)

# pyconfig.h的路徑
include_directories(/usr/include/python3.4m)

# 注意庫名稱要和代碼中指定的一致
add_library(hello SHARED main.cpp)
# 連接
target_link_libraries(
        hello
        python3.4m
        boost_python3
)

5.結(jié)構(gòu)體和對象

代碼

BOOST_PYTHON_MODULE(class_example){
    class_<Student>("Student")
            .def("sayHello",&Student::sayHello);
}

6.對象的構(gòu)造函數(shù)

BOOST_PYTHON_MODULE(constructor){
    class_<Student>("Student")//無參
            .def(init<string>())//一個參數(shù)
            .def(init<string,int>())//兩個參數(shù)
            .def("sayHello",&Student::sayHello);
}

注意:沒有構(gòu)造函數(shù) 可以使用no_init表示

7.成員變量

def_readonly:只讀屬性
def_readwrite:讀寫屬性
add_property:將get和set方法包裝成屬性

代碼

class Student {
private:
    double mNumber;
public:
    //不可修改
    const string  name;
    //可讀可寫
    int age;

    Student();

    ~Student();

    //獲取mNumber
    double getNumber();

    //設(shè)置mNumber
    void setNumber(double value);

    //打印三個屬性
    void printMembers();
};

可以把getNumber和setNumber包裝成一個屬性

包裝:

BOOST_PYTHON_MODULE(members){
    class_<Student>("Student")
            .def_readonly("name",&Student::name)
            .def_readwrite("age",&Student::age)
            //必須要先get再set
            .add_property("number",&Student::getNumber,&Student::setNumber)
            .def("printMembers",&Student::printMembers);
}

8.多態(tài)

class Base
{
public:
    virtual std::string name() const { return "Base"; }
    virtual ~Base() {}
};

class Derived : public Base
{
public:
    virtual std::string name() const { return "Derived"; }
};

void fb(Base *b)
{
    std::cout << b->name() << " called." << std::endl;
}

void fd(Derived *d)
{
    std::cout << "Derived " << d->name() << " called." << std::endl;
}

包裝:

BOOST_PYTHON_MODULE(inheritance)
{
    class_<Base, boost::noncopyable>("Base")//沒有拷貝構(gòu)造
            .def("name", &Base::name);

    //對于繼承,則需指明繼承對象
    class_<Derived, bases<Base> >("Derived");

    def("fb", fb);
    def("fd", fd);
    
}

9.靜態(tài)成員和靜態(tài)方法

指定靜態(tài)方法

staticmethod

#include <iostream>
using namespace std;

class Example {
public:
    static string name;

    static void sayHello();
};

string Example::name = "張三";

void Example::sayHello(){
    cout<<"hello world"<<endl;
}

#include <boost/python.hpp>

using namespace boost::python;

boost包裝:

BOOST_PYTHON_MODULE (static_example) {
    class_<Example>("Example", no_init)
            .def("sayHello", &Example::sayHello)
            .def_readwrite("name",&Example::name)
            .staticmethod("sayHello");
}

python調(diào)用:

from static_example import Example
# 調(diào)用靜態(tài)方法
Example.sayHello()
# 調(diào)用靜態(tài)屬性
print(Example.name)

注意:

對于靜態(tài)屬性不用特別處理,靜態(tài)方法需要特殊處理

10.方法重載

對于方法重載分為兩種:

1.普通方法重載

2.默認(rèn)參數(shù)函數(shù)(類成員以及普通函數(shù))

代碼:

class Student{
public:
    void sayHello(){
        cout<<"空 sayHello"<<endl;
    }
    void sayHello(int age){
        cout<<"int sayHello"<<endl;
    }
    void sayHello(string name,int age){
        cout<<"string和int sayHello"<<endl;
    }

    void haha(string name,int age=10,double score = 34){
        cout<<"調(diào)用了默認(rèn)參數(shù)的函數(shù)"<<endl;
    }
};

//普通默認(rèn)參數(shù)函數(shù)
void hehe(string name,int age=1,double score = 1.123){
    cout<<"調(diào)用了sayHello"<<name<<age<<score<<endl;
}

處理結(jié)果:

//處理默認(rèn)參數(shù)函數(shù)的重載問題
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(haha_overloads, haha, 1, 3)
//處理普通方法重載
BOOST_PYTHON_FUNCTION_OVERLOADS(sayHello_overloads, hehe, 1, 3)
    
BOOST_PYTHON_MODULE(overload)
{
    //普通重載的方法
    void        (Example::*d1)()             = &Example::doit;
    std::string (Example::*d2)(unsigned int) = &Example::doit;
    void        (Example::*d3)(std::string)  = &Example::doit;
    
    class_<Example>("Example")
            .def("__str__", &Example::print)
            .def("doit", d1)
            .def("doit", d2)
            .def("doit", d3)
            //默認(rèn)參數(shù)的方法重載
            .def("makeIt", &Example::makeIt, makeIt_overloads());
}

11.枚舉

使用enum_包裝枚舉

格式:

enum_<name>("name")
            .value("value1", value1)
            .value("value2", value2)
            .value("value3", value3);

boost封裝:

#include <boost/python.hpp>

using namespace boost::python;

enum color { red = 1, green = 2, blue = 4 };

BOOST_PYTHON_MODULE(enum_example)
{
    enum_<color>("color")
            .value("red", red)
            .value("green", green)
            .value("blue", blue);
}

12.指針

只關(guān)注返回指針

參數(shù)指針不用處理

return_value_policy:返回策略

manage_new_object:指針返回策略

#include <boost/python.hpp>

using namespace boost::python;

class Student {
public:
    //靜態(tài)方法,返回當(dāng)前對象的指針
    static Student *create() { return new Student; }
};

BOOST_PYTHON_MODULE (pointer) {
    class_<Student>("Student", no_init)
            //return_value_policy返回策略  
            //設(shè)置manage_new_object通過python管理c++通過new創(chuàng)建的對象
            .def("create", &Student::create, return_value_policy<manage_new_object>())
            .staticmethod("create");
}

13.智能指針

智能指針

#include <string>
#include <boost/python.hpp>
#include <memory>

using namespace std;
using namespace boost::python;

struct Student {
    static shared_ptr<Student> create() { return shared_ptr<Student>(new Student); }

    std::string hello() { return "Just nod if you can hear me!"; }
};

BOOST_PYTHON_MODULE (smart_ptr) {
    class_<Student, shared_ptr<Student>>("Student")
            .def("create", &Student::create)
            .staticmethod("create")
            .def("hello", &Student::hello);
}

14.pure virtual

#include <boost/python.hpp>
#include <iostream>
using namespace boost::python;
using namespace std;

class Father{
public:
    virtual void sayHello()=0;
    void haha(){
        cout<<"father haha"<<endl;
    }
};

class Son:public Father{
public:
    void sayHello()override {
        cout<<"hello"<<endl;
    }
};


BOOST_PYTHON_MODULE(purevirtual)
{
    class_<Father, boost::noncopyable>("Father",no_init)
            .def("haha",&Father::haha);
    class_<Son, bases<Father>>("Son")
    .def("sayHello",&Son::sayHello);
}

15.vector

通過模板vector_indexing_suite導(dǎo)出

c++中的vector不等于python中的list

Boost.python中有與python的list對應(yīng)的東西,是boost::python::list

如果我們不打算修改原有的C++代碼,又想調(diào)用以vector為參數(shù)的函數(shù),該怎么辦呢?

  1. 在C++代碼里對以vector為參數(shù)的函數(shù)進(jìn)行一層封裝,封裝為以boost::python::list為參數(shù)的函數(shù),導(dǎo)出封裝后的函數(shù)

  2. 直接導(dǎo)出vector<T>類型。此時vector<T>本身作為一個類型被導(dǎo)出給python代碼,與普通的類具有同等地位

它通過模板vector_indexing_suite<std::vector<T> >()導(dǎo)出,自動實(shí)現(xiàn)了append,slice,len等方法,在python里可以像使用list那樣操作這個被導(dǎo)出的vector類

#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <vector>

using namespace std;
using namespace boost::python;

void sayHello(vector<int> params){
    cout<<params.at(0)<<endl;
}

BOOST_PYTHON_MODULE(vector_param)
{
    //vector name
    class_<std::vector<int>>("int_vector")
            .def(vector_indexing_suite<std::vector<int>>());
    def("sayHello", sayHello);
}

16.map

map通過map_indexing_suite模板導(dǎo)出

#include <boost/python.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <map>
using namespace std;
using namespace boost::python;

void sayHello(map<int,int> param){
    cout<<param.size()<<endl;
}

BOOST_PYTHON_MODULE(map_sample)
{
    class_<std::map<int,int>>("int_map")
            .def(map_indexing_suite<std::map<int,int>>());
    def("sayHello", sayHello);
}

python調(diào)用

from map_param import sayHello,int_map
m = int_map()
# insert item
m.__setitem__(10,20)
sayHello(m)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容