5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

boostpythonでC++の関数をpythonに公開する

Posted at

はじめに

C++をpythonで使用するためにboostpythonの力を使ってC++をpythonに公開してみます。

環境

  • Ubuntu:18.04.2
  • python3:3.6.8

環境作成については、ubuntuでboost pythonを使用するに載せています。

C++関数の公開

C++の関数をpythonに公開するときは、C++の関数を作成してboostpythonのdefを使用して公開します。

ポイント

BOOST_PYTHON_MODULE(pythonへ公開するモジュール名) {
    boost::python::def("pythonへ公開する関数名", &実際に実行する関数名);
}

実例

1.公開する関数として引数 + " hello world"を返却する関数を作成します。

CppMod.cpp
std::string hello(std::string name) {
    return name + " hello world";
}

2.python側に公開するための設定をします。

CppMod.cpp
BOOST_PYTHON_MODULE(CppMod) {
    boost::python::def("hello", &hello);
 }

3.python側で呼び出します。

>>> import CppMod
>>> CppMod.hello("mink")
'mink hello world'

C++クラスの公開

C++クラス関数の公開

C++とpythonはクラスが使用できるのでC++のクラスをそのままpythonに公開することで、クラス変数やクラスの関数を使用できるように調べました。C++のクラスを作成して、それをboostpythonのclass_()を使用することでpythonに公開します。

ポイント

BOOST_PYTHON_MODULE(pythonへ公開するモジュール名) {
    boost::python::class_<実際に実行するクラス名>(pythonへ公開するクラス名)
    .def("pythonへ公開する関数名", &実際に実行する関数名);
}

実例

1.公開するクラスとmemberに値を入れる関数とmember + " hello"とmember + " bye"を返却する関数を作成します。

CppMod.cpp
#include <boost/python.hpp>
class Greeting {
    private:
    std::string member = "";
    
    public:
    void SetName(std::string name) {
        member = name;
     }
     std::string hello() {
         return member + " hello";  
     }
     std::string bye() {
         return member + " bye";
     }
 };

2.python側に公開するための設定をします。classに公開したクラスを設定して、defでクラス内の関数を設定します。

CppMod.cpp
BOOST_PYTHON_MODULE(CppMod) {
    boost::python::class_<Greeting>("greeting")
    .def("setName", &Greeting::SetName)
    .def("hello", &Greeting::hello)
    .def("bye", &Greeting::bye);
 }

3.python側で呼び出します。python側は普通のクラスのように呼び出します。

pyMod.py
import CppMod

cppClass = CppMod.greeting()
print(cppClass.hello())
print(cppClass.bye())

cppClass.setName("mink")
print(cppClass.hello())
print(cppClass.bye())

4.pythonを実行する。

```
# python3 pyMod.py
 hello
 bye
mink hello
mink bye
```

C++クラス変数の公開

上の方法ではmember変数にセッター経由で変更しました。オブジェクト指向的にはそれでも良いかもしれないですがpython風に使うためにはクラス.変数でアクセスできるようにしたいケースがあるのでその方法を調べました。変数をpublicにしてboostpythonのadd_propertyを使用することでpythonに公開します。

ポイント

BOOST_PYTHON_MODULE(pythonへ公開するモジュール名) {
    boost::python::class_<実際に実行するクラス名>(pythonへ公開するクラス名)
    .add_property(pythonへ公開する変数名, &実際に使用する変数名, &セッター)
    .def("pythonへ公開する関数名", &実際に実行する関数名);
}

実例

1.public変数があるクラスを作成します。。

CppMod.cpp
#include <boost/python.hpp>
class Greeting {
    public:
    
    std::string member = "";
    
    void SetName(std::string name) {
        member = name;
     }
     std::string hello() {
         return member + " hello";  
     }
     std::string bye() {
         return member + " bye";
     }
 };

2.python側に公開するための設定をします。classに公開したクラスを設定して、add_propertyでクラス内の変数を設定し、defでクラス内の関数を設定します。add_propertyの第3引数を設定しない場合は、参照のみ可能な変数としてpythonに公開されます。

CppMod.cpp
BOOST_PYTHON_MODULE(CppMod) {
    boost::python::class_<Greeting>("greeting")
    .add_property("member", &Greeting::member, &Greeting::SetName)
    .def("hello", &Greeting::hello)
    .def("bye", &Greeting::bye);
 }

3.python側で呼び出します。python側は普通のクラスのように呼び出します。

pyMod.py
import CppMod

cppClass = CppMod.greeting()

cppClass.member = "mink"
print(cppClass.hello())
print(cppClass.bye())

cppClass.member = "dog"
print(cppClass.hello())
print(cppClass.bye())

4.pythonを実行する。

```
# python3 pyMod.py
mink hello
mink bye
dog hello
dog bye
```

小ネタ

pythonの特殊クラス

pythonには init や _____str_____のように特殊な関数がありますが、それも普通にC++の関数を公開すると特殊関数として動作してくれます。

CppMod.cpp
class Greeting {
    public:
    std::string member = "mink";
    std::string __str__() {
        return "str: member=" + member;
    }
};

BOOST_PYTHON_MODULE(CppMod) {
    boost::python::class_<Greeting>("greeting")
    .def("__str__", &Greeting::__str__);
}
pyMod.py
import CppMod
cppClass = CppMod.greeting()
print(cppClass)
# python3 pyMod.py
str: member=mink

C++には存在しない型

dictやtupleは、C++には存在しないがboostpython内でその型を定義してくれています。
それを利用してC++でもpython風にdictやtupleを扱えます。

CppMod.cpp
# include <boost/python.hpp>
# include <iostream>

using namespace boost::python;

class Greeting {
    public:
    void viewType(dict pyDict, list pyList, tuple pyTuple) {

        std::cout << "Dict View"<< std::endl;
        int dictLen = len(pyDict);
        list keyList = pyDict.keys();
        for(int i=0; i <dictLen; i++) {
            std::string key = extract<std::string>(keyList[i]);
            int val = extract<int>(pyDict[key]);
            std::cout << "key=" << key << " val=" << val << std::endl;
        }

        std::cout << "List View"<< std::endl;
        int listLen = len(pyList);
        for(int i=0; i <listLen; i++) {
            int val = boost::python::extract<int>(pyList[i]);
            std::cout << "val=" << val << std::endl;
        }

        std::cout << "Tuple View"<< std::endl;
        std::string val1 = extract<std::string>(pyTuple[0]);
        std::string val2 = extract<std::string>(pyTuple[1]);
        std::cout << "val1=" << val1 << " val2=" << val2 << std::endl;
    }
};

BOOST_PYTHON_MODULE(CppMod) {
    boost::python::class_<Greeting>("greeting")
    .def("viewType", &Greeting::viewType);
}

pyMod.py
import CppMod

cppClass = CppMod.greeting()

cppClass.viewType(
    {"a":1,"b":2},
    [1,2,3],
    ("a","b")
)
# python3 pyMod.py
Dict View
key=a val=1
key=b val=2
List View
val=1
val=2
val=3
Tuple View
val1=a val2=b
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?