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


本日は

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

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

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

前提

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

導入

  1. Visual Studio を起動します.とりあえずC++の空のプロジェクトを作成します.(ここでは callpyfromc とします.)下記の手順に従ってプロジェクトのプロパティを変更していきます.
  2. VC++ ディレクトリで Pythonのヘッダー,ライブラリのパスと Boostのヘッダーとライブラリのパスを追加します. 例えば次の図のように設定します.
  3. C/C++ -> コード生成 において ランタイム ライブラリマルチスレッド(/MT) に変更します.(下図参照)
  4. コードを書きます.

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

  • 行頭におまじないを追加する
  • numpy.hpp の場所がかわっているのでそれを修正したというところです.
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コードを作成します.

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)

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


> callpython.exe

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

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

わかっていないこと

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

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