Numpy 付 Boost 1.66.0 の導入 Visual Studio 2017 対応版


本日は

Boost を使ってC++をPythonから呼び出したい.という願いを実現するために

PythonからC++を呼び出してnumpyを使う

の内容に従って次の組み合わせでトライしてみます,

  • Visual Studio 2017
  • Boost (1.66.0)

Boost の導入

  1. http://www.boost.org/ にアクセス. ページ後半にあるダウンロードできるページに飛びます.
  2. boost_1_66_0.zip のダウンロードと展開 下の図にあるようにWindowsに対応するものをダウンロードします. 解凍したら簡単のためにCドライブの直下に置きます.
  3. コマンドの実行 コマンドプロンプトを起動して次を実行します.
> bootstrap.bat 
> b2 toolset=msvc threading=multi variant=debug,release link=static runtime-link=static address-model=64 --stagedir=stage/x64 -j 8

-j はビルドに使うためのコア数に相当するようなので各自のPCの環境に応じて変更します.

その他のオプションなどは
boostの導入(Windows10,Visual Studio 2015)
が役に立ちそうです.
4. 生成物の確認

--stagedir=stage/x64 と指定していたのでライブラリなどの出力結果を確認します.
- libboost_numpy_....
- libboost_python3_....

のようなものがあれば幸せになります.

Visual Studio 2017の起動

以下プラットフォームを x64 構成を Release とした場合に限って説明します.

C++ -> 空のプロジェクト を選択しとりあえずプロジェクト名を callcfrompy とします.この名前はPythonのモジュールとして使いたい名前を指定しておきます.この場合後で

import callcfrompy

のようにできます.
この後プロジェクトのプロパティの設定をしますが Pybind11で行ったような設定と似ている部分もあります.

See also

プロジェクトのプロパティの設定

構成プロパティ -> 全般に進み 次の項目を変更します.

  • 構成の種類を ダイナミック ライブラリ(.dll)
  • ターゲット拡張子 .pyd

C/C++ -> コード生成 において ランタイム ライブラリマルチスレッド(/MT) に変更.

VC++ ディレクトリ において インクルードディレクトリとライブラリディレクトリを各自の環境に合わせて設定.

私が追加したのはつぎのとおり:

  • インクルードディレクトリ
    • C:\boost_1_66_0
    • C:\Miniconda3\include
  • ライブラリディレクトリ
    • C:\boost_1_66_0\stage\x64\lib
    • C:\Miniconda3\libs

サンプルコードを書く

元ネタは C++でPythonを拡張するためのBoost.NumPyチュートリアル(実践編) からとのことなので引用します.

/*
Call C++ From Python
Reference:
http://tadaoyamaoka.hatenablog.com/entry/2017/05/25/234934
https://qiita.com/takuya-ki/items/3555ab17f9cea534e13b
https://qiita.com/termoshtt/items/81eeb0467d9087958f7f
*/

#define BOOST_PYTHON_STATIC_LIB
#define BOOST_NUMPY_STATIC_LIB
#include <boost/python/numpy.hpp>
#include <stdexcept>
#include <algorithm>

namespace p = boost::python;
namespace np = boost::python::numpy;

/* 2倍にする */
void mult_two(np::ndarray a) {
    int nd = a.get_nd();
    if (nd != 1)
        throw std::runtime_error("a must be 1-dimensional");
    size_t N = a.shape(0);
    if (a.get_dtype() != np::dtype::get_builtin<float>())
        throw std::runtime_error("a must be float32 array");
    float *p = reinterpret_cast<float *>(a.get_data());
    std::transform(p, p + N, p, [](float x) { return 2 * x; });
}

BOOST_PYTHON_MODULE(callcfrompy) {
    Py_Initialize();
    np::initialize();
    p::def("mult_two", mult_two);
}

これをソースに追加してビルドします.

callcfrompy.pyd なるものが生成されたらOKです.

あとはこれと同じディレクトリ上に下記の呼び出しコードを作ります.

test.py
from callcfrompy import mult_two
import numpy as np


def main():
    arr = np.array([1, 2, 3, 4, 5]).astype(np.float32)
    mult_two(arr)
    print(arr)

if __name__ == '__main__':
    main()

実行結果として配列の値が2倍になって入ればOKです.