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

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

## 環境
 - Ubuntu:18.04.2
 - python3:3.6.8

環境作成については、[ubuntuでboost pythonを使用する](https://qiita.com/mink0212/items/5a429bdc70bef2245413)に載せています。


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

#### ポイント
``` C++
BOOST_PYTHON_MODULE(pythonへ公開するモジュール名) {
    boost::python::def("pythonへ公開する関数名", &実際に実行する関数名);
}
```

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

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

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

   ``` C++: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_<T>()を使用することでpythonに公開します。

#### ポイント

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

#### 実例

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

   ``` C++: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でクラス内の関数を設定します。
   
   ``` C++: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側は普通のクラスのように呼び出します。

   ``` 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に公開します。

#### ポイント

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

#### 実例

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

   ``` C++: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に公開されます。
   
   ``` C++: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側は普通のクラスのように呼び出します。

   ``` 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++の関数を公開すると特殊関数として動作してくれます。

``` 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__);
}
```

``` python:pyMod.py
import CppMod
cppClass = CppMod.greeting()
print(cppClass)
```

```
# python3 pyMod.py
str: member=mink
```

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

``` C++: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);
}

```

``` python: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
```