モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)
目次目次を開く/閉じる

pybind11 で Python から C++ を呼び出す

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2019/08/20
最終更新最終更新
2022/01/29
記事区分記事区分
一般公開

目次

    アカウント プロフィール画像 (サイドバー)

    インフラ構築と自動化が得意。TerraformとAnsibleでインフラを自動構築するお仕事が多め

    0
    ステッカーを贈るとは?

    C++ を Python から利用する方法の一つに pybind11 があります。C++11 をサポートするコンパイラが必要です。サンプルコードを記載します。

    簡単なサンプル

    C++11 コードをビルドして、Python から利用するための .so ファイルを生成します。ビルド方法は複数ありますがここでは CMake を利用します。

    Building with CMake

    $ git init
    $ git submodule add https://github.com/pybind/pybind11.git
    $ mkdir build
    $ ls
    CMakeLists.txt  build  example.cpp  main.py  pybind11
    
    $ cat CMakeLists.txt
    cmake_minimum_required(VERSION 2.8.12)
    project(example)
    
    add_subdirectory(pybind11)
    pybind11_add_module(example example.cpp)
    
    $ cat example.cpp
    #include <pybind11/pybind11.h>
    
    int add(int i, int j) {
        return i + j;
    }
    
    PYBIND11_MODULE(example, m) {
        m.doc() = "pybind11 example plugin"; // optional module docstring
        m.def("add", &add, "A function which adds two numbers");
    }
    
    $ cat main.py
    import example
    print example.add(1, 1)
    

    example.so のビルド

    cd build
    cmake -DPYTHON_EXECUTABLE=`which python` ..
    make
    

    実行例

    $ PYTHONPATH=$PYTHONPATH:build python ../main.py
    2
    

    Python が複数インストールされている場合の注意点

    インストールされている Python のバージョンは CMake が自動的に検出します。検出された情報は CMakeCache.txt 内に記載されます。

    $ grep python CMakeCache.txt  | head -1
    PYTHON_EXECUTABLE:FILEPATH=/path/to/python3.6
    

    複数バージョンの Python が存在する環境においては、利用する Python 実行ファイルへのパスを指定します

    cmake -DPYTHON_EXECUTABLE=/usr/bin/python2 ..
    

    以下のようにバージョンを指定する方法もあります

    cmake -DPYBIND11_PYTHON_VERSION=2.7 ..
    

    CMakeLists.txt 内で指定することもできます。

    set(PYBIND11_PYTHON_VERSION 2.7)
    

    または

    set(PYTHON_EXECUTABLE /usr/bin/python2)
    

    C++ バージョンの指定

    add_subdirectory より前に指定します。

    set(PYBIND11_CPP_STANDARD -std=c++11)
    

    または

    set(PYBIND11_CPP_STANDARD -std=c++14)
    

    関数のバインディング

    int add(int i, int j) {
        return i + j;
    }
    

    コメントなし、キーワード引数なし、デフォルト引数なし

    m.def("add", &add);
    

    キーワード引数

    namespace py = pybind11;
    m.def("add", &add, py::arg("i"), py::arg("j"));
    

    または

    namespace py = pybind11;
    using namespace py::literals;
    m.def("add", &add, "i"_a, "j"_a);
    

    Python

    print example.add(i=1, j=1)
    

    デフォルト引数

    namespace py = pybind11;
    m.def("add", &add, py::arg("i") = 1, py::arg("j") = 1);
    

    または

    namespace py = pybind11;
    using namespace py::literals;
    m.def("add", &add, "i"_a=1, "j"_a=1);
    

    Python

    print example.add()
    

    値のバインディング

    m.attr("xxx") = 123;
    
    namespace py = pybind11;
    py::object yyy = py::cast("mystr");
    m.attr("yyy") = yyy;
    

    Python

    print example.xxx
    print example.yyy
    

    クラスのバインディング

    コンストラクタ、メンバ関数

    example.cpp

    #include <pybind11/pybind11.h>
    
    struct Pet {
        Pet(const std::string &name) : name(name) { }
        void setName(const std::string &name_) { name = name_; }
        const std::string &getName() const { return name; }
    
        std::string name;
    };
    
    namespace py = pybind11;
    
    PYBIND11_MODULE(example, m) {
        py::class_<Pet>(m, "Pet") // 構造体であっても class_ でバインディングします。
            .def(py::init<const std::string &>()) // コンストラクタの引数です。
            .def("setName", &Pet::setName)
            .def("getName", &Pet::getName);
    }
    

    main.py

    import example
    p = example.Pet('xxx')
    print p
    print p.getName()
    p.setName('yyy')
    print p.getName()
    

    補足

    • name(name)std::string のコンストラクタを利用した、メンバ変数の初期化です
    • getName() const は const メンバ関数です
    • setName および getName はインライン関数です

    静的メンバ関数

    静的メンバ関数をバインディングするためには def ではなく def_static を利用します。

    example.cpp

    #include <pybind11/pybind11.h>
    #include <iostream>
    
    class MyClass {
    public:
        static void Show() { std::cout << m_mystr << std::endl; }
    private:
        static std::string m_mystr;
    };
    
    std::string MyClass::m_mystr = "hello";
    
    PYBIND11_MODULE(example, m) {
        pybind11::class_<MyClass>(m, "MyClass")
            .def_static("Show", &MyClass::Show);
    }
    

    main.py

    import example
    example.MyClass.Show() #=> hello
    

    メンバ変数、静的メンバ変数

    メンバ変数

    .def_readwrite("name", &Pet::name)
    

    メンバ定数

    .def_readonly("name", &Pet::name)
    

    private メンバ変数

    .def_property("name", &Pet::getName, &Pet::setName)
    

    private メンバ定数

    .def_property_readonly("name", &Pet::getName)
    

    静的メンバ変数および静的メンバ定数のバインディングには以下を利用します。

    def_readwrite_static()
    def_readonly_static()
    def_property_static()
    def_property_readonly_static()
    

    継承

    #include <pybind11/pybind11.h>
    
    struct MyClass {
        MyClass(const std::string &name) : name(name) { }
        const std::string getName() const { return name; }
        std::string name;
    };
    
    struct MySubClass : MyClass {
        MySubClass(const std::string &name) : MyClass(name) { }
    };
    
    PYBIND11_MODULE(example, m) {
    
        pybind11::class_<MyClass>(m, "MyClass")
            .def(pybind11::init<const std::string &>())
            .def("getName", &MyClass::getName)
            .def_readwrite("name", &MyClass::name);
    
        pybind11::class_<MySubClass, MyClass>(m, "MySubClass")
            .def(pybind11::init<const std::string &>());
    
        // あるいは以下のようにもできます
        // myClass.def(pybind11::init<const std::string &>())
        //     .def("getName", &MyClass::getName)
        //     .def_readwrite("name", &MyClass::name);
    
        // pybind11::class_<MySubClass>(m, "MySubClass", myClass)
        //     .def(pybind11::init<const std::string &>());
    }
    

    Python

    import example
    sub = example.MySubClass('myname')
    print sub.name #=> myname
    print sub.getName() #=> myname
    

    ラムダ式によるバインディング

    C++ 側で実装されていない関数をラムダ式で定義してバインディングしたり、C++ における実装をラムダ式で拡張して同名でバインディングしたりできます。Python の __repr__ を実装する場合にも使うことができます。

    .def("__repr__",
         [](const Pet &a) {
             return "<example.Pet named '" + a.name + "'>";
         }
    );
    

    関数オーバーロード

    関数ポインタにキャストすることでオーバーロードされた関数を pybind できます。

    #include <pybind11/pybind11.h>
    #include <iostream>
    
    void f(int i) {
        std::cout << "f: " << i << std::endl;
    }
    
    void f(const std::string &s) {
        std::cout << "f2: " << s << std::endl;
    }
    
    PYBIND11_MODULE(example, m) {
    
        m.def("f", (void (*)(int)) &f)
            .def("f", (void (*)(const std::string &)) &f);
    
    }
    

    Python

    import example
    example.f(123) #=> f: 123
    example.f('aaa') #=> f2: aaa
    

    C++14 に対応したコンパイラの場合は pybind11::overload_cast が利用できます。

    m.def("f", pybind11::overload_cast<int>(&f))
        .def("f", pybind11::overload_cast<const std::string &>(&f));
    

    列挙体

    単純な列挙体は以下のように pybind します。

    #include <pybind11/pybind11.h>
    
    enum MyEnum {
        VALUE_A,
        VALUE_B,
        VALUE_C
    };
    
    PYBIND11_MODULE(example, m) {
    
        pybind11::enum_<MyEnum>(m, "MyEnum")
            .value("VALUE_A", MyEnum::VALUE_A)
            .value("VALUE_B", MyEnum::VALUE_B)
            .value("VALUE_C", MyEnum::VALUE_C);
    }
    

    Python

    import example
    b = example.MyEnum.VALUE_B
    int(b) #=> 1
    b.name #=> u'VALUE_B'
    
    In [11]: example.MyEnum.__members__
    Out[11]: 
    {u'VALUE_A': MyEnum.VALUE_A,
     u'VALUE_B': MyEnum.VALUE_B,
     u'VALUE_C': MyEnum.VALUE_C}
    

    クラス内の列挙体は以下のようにします。

    #include <pybind11/pybind11.h>
    
    struct Pet {
        enum Kind {
            Dog = 0,
            Cat
        };
    
        Pet(const std::string &name, Kind type) : name(name), type(type) { }
    
        std::string name;
        Kind type;
    };
    
    PYBIND11_MODULE(example, m) {
    
        pybind11::class_<Pet> pet(m, "Pet");
    
        pet.def(pybind11::init<const std::string &, Pet::Kind>())
            .def_readwrite("name", &Pet::name)
            .def_readwrite("type", &Pet::type);
    
        pybind11::enum_<Pet::Kind>(pet, "Kind")
            .value("Dog", Pet::Kind::Dog)
            .value("Cat", Pet::Kind::Cat)
            .export_values();
    }
    

    Python

    from example import Pet
    p = Pet('Lucy', Pet.Cat)
    print p.type #=> Kind.Cat
    print p.type.name #=> u'Cat'
    print int(p.type) #=> 1
    print Pet.Kind.__members__ #=> {u'Dog': Kind.Dog, u'Cat': Kind.Cat}
    

    C++11 スマートポインタ

    unique_ptr

    #include <pybind11/pybind11.h>
    
    #include <memory>
    #include <iostream>
    
    struct MyClass {
        ~MyClass() {
            std::cout << "myclass destroyed" << std::endl;
        }
    };
    
    std::unique_ptr<MyClass> createMyClass() {
        return std::unique_ptr<MyClass>(new MyClass());
    }
    
    PYBIND11_MODULE(example, m) {
        pybind11::class_<MyClass>(m, "MyClass")
            .def(pybind11::init<>());
    
        m.def("createMyClass", &createMyClass);
    }
    

    Python

    from example import createMyClass
    
    obj = createMyClass()
    print 'obj exists'
    obj = None
    print 'obj is none'
    

    実行例

    obj exists
    myclass destroyed
    obj is none
    

    shared_ptr

    #include <pybind11/pybind11.h>
    
    #include <memory>
    #include <iostream>
    
    class Shared {
    public:
        ~Shared() {
            std::cout << "shared destroyed" << std::endl;
        }
    };
    
    class MyClass {
    public:
        MyClass() : shared(std::make_shared<Shared>()) { }
        ~MyClass() {
            std::cout << "myclass destroyed" << std::endl;
        }
        std::shared_ptr<Shared> getShared() { return shared; }
    private:
        std::shared_ptr<Shared> shared;
    };
    
    PYBIND11_MODULE(example, m) {
        pybind11::class_<Shared, std::shared_ptr<Shared>>(m, "Shared");
    
        pybind11::class_<MyClass, std::shared_ptr<MyClass>>(m, "MyClass")
           .def(pybind11::init<>())
           .def("getShared", &MyClass::getShared);
    }
    

    Python

    from example import MyClass
    
    shared = MyClass().getShared()
    print 'shared exists'
    shared = None
    print 'shared none'
    

    実行例

    myclass destroyed
    shared exists
    shared destroyed
    shared none
    

    その他

    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    インフラ構築と自動化が得意。TerraformとAnsibleでインフラを自動構築するお仕事が多め

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    さらに詳しく →
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      関連記事