C++ から Numpy付 Boost を介して Pythonを呼ぶ （Visual Studio 2017 対応）

# 本日は

Visual Studio 2017 と　Boost を利用して Python 側から C++ を呼び出す方法

- [Numpy 付 Boost 1.66.0 の導入 Visual Studio 2017 対応版]
(https://qiita.com/SatoshiTerasaki/items/6ed55551bbdef79ce3fc)
- [conda を利用した Numpy 付 Boost の導入 Visual Studio 2017 対応](https://qiita.com/SatoshiTerasaki/items/1a8ba37815cad2b7e9a9)

を書きました．今度は C++ 側から Pythonを呼ぶ方法を紹介します．Boostのバージョンは 1.66.0 です．

今回は次のReferenceをガイドとして試してみます．

- [C++からPythonを叩きつつ、boost.numpyを使ってC++とPython間でndarrayをやりとりする](http://segafreder.hatenablog.com/entry/2016/12/18/110423)

# 前提

- [conda を利用した Numpy 付 Boost の導入 Visual Studio 2017 対応](https://qiita.com/SatoshiTerasaki/items/1a8ba37815cad2b7e9a9)

を読んで Numpy 付 Boost が導入されていること．

# 導入 

1. Visual Studio を起動します．とりあえずC++の空のプロジェクトを作成します．（ここでは `callpyfromc` とします．）下記の手順に従ってプロジェクトのプロパティを変更していきます．
2. VC++ ディレクトリで Pythonのヘッダー，ライブラリのパスと Boostのヘッダーとライブラリのパスを追加します． 例えば次の図のように設定します．
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135978/1d0ff7ca-30a3-5ffa-6596-0ef7a9215b2f.png)
3. C/C++ -> コード生成 において `ランタイム ライブラリ` を `マルチスレッド（/MT）` に変更します．（下図参照）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135978/b8f9399a-c03c-9879-b756-57e2ad94b390.png)
4. コードを書きます．

Reference のコードをBoost 1.66.0
と適合するようにします．変更点は主に次の通り．

- 行頭におまじないを追加する
- numpy.hpp の場所がかわっているのでそれを修正したというところです．

```C++:sample.cpp
//http://segafreder.hatenablog.com/entry/2016/12/18/110423

#define BOOST_PYTHON_STATIC_LIB
#define BOOST_NUMPY_STATIC_LIB

#include <iostream>
#include <string>
#include <fstream>
#include <streambuf>
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
#include <list>


#define MAX_X 100 //受け渡したい行列のサイズ。

namespace np = boost::python::numpy;

int main() {

	//Python、numpyモジュールの初期化
	Py_Initialize();
	np::initialize();

	//名前空間の確保
	auto main_ns = boost::python::import("__main__").attr("__dict__");

	//Pythonスクリプトの読み込み
	std::ifstream ifs("mat_numpy.py");
	std::string script((std::istreambuf_iterator<char>(ifs)),
		std::istreambuf_iterator<char>());

	//100x100行列の準備
	boost::python::tuple shapeA = boost::python::make_tuple(MAX_X, MAX_X);
	np::ndarray A = np::zeros(shapeA, np::dtype::get_builtin<double>());
	for (int i = 0; i != MAX_X; i++) {
		for (int j = 0; j != MAX_X; j++) {
			A[i][j] = i + j;
		}
	}

	//mat_numpy.mulの実行
	boost::python::exec(script.c_str(), main_ns);
	auto func = main_ns["mul"];
	auto pyresult_numpy = func(A);

	//結果の受け取り
	//stl_input_iteratorを使ってタプル全要素を受け取る
	boost::python::stl_input_iterator<np::ndarray> begin(pyresult_numpy), end;
	std::list<np::ndarray> pyresult_list(begin, end);

	for (auto itr = pyresult_list.begin(); itr != pyresult_list.end(); ++itr) {
		double *p = reinterpret_cast<double *>((*itr).get_data());
		//ndarrayでは基本的にメモリは連続領域上に保持されるので、
		//各要素には[]演算子を使ってアクセスできる
		std::cout << p[0] << ',' << p[1] << ',' << p[2] << ',' << p[MAX_X * MAX_X - 1] << std::endl;
	}
	return 0;
}
```

ビルドが成功すると `callpyfromc.exe` が生成されますので，それと同階層に次のPythonコードを作成します．


```python:mat_mum.py
# nはnumpy.ndarray
def mul(n):
    print("from python")
    print("received: " + str(n))
    print("end from python")

    # ndarrayのタプルを返す
    return (2 * n, 3* n)
```

コマンドプロンプトを起動して

```shell-session

> callpython.exe
```

が実行されると成功です．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/135978/2da782aa-4dd0-155c-7a86-638c0f9d8d76.png)

とりあえずできたっぽいです．

# わかっていないこと

Visual Studio 上で F5 などでデバッグ実行するとハングアップする．

どうやってデバッグすればいいんだ．