はじめに
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"を返却する関数を作成します。
std::string hello(std::string name) {
return name + " hello world";
}
2.python側に公開するための設定をします。
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"を返却する関数を作成します。
#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でクラス内の関数を設定します。
BOOST_PYTHON_MODULE(CppMod) {
boost::python::class_<Greeting>("greeting")
.def("setName", &Greeting::SetName)
.def("hello", &Greeting::hello)
.def("bye", &Greeting::bye);
}
3.python側で呼び出します。python側は普通のクラスのように呼び出します。
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変数があるクラスを作成します。。
#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に公開されます。
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側は普通のクラスのように呼び出します。
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++の関数を公開すると特殊関数として動作してくれます。
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__);
}
import CppMod
cppClass = CppMod.greeting()
print(cppClass)
# python3 pyMod.py
str: member=mink
C++には存在しない型
dictやtupleは、C++には存在しないがboostpython内でその型を定義してくれています。
それを利用してC++でもpython風にdictやtupleを扱えます。
# 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);
}
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