Visual Studio で Python 用の C++ 拡張機能を作成する

この記事では、双曲正接を計算する CPython 用の C++ 拡張モジュールをビルドし、Python コードからそれを呼び出す手順について説明します。 Python では最初にルーチンを実装して、C++ で同じルーチンを実装した場合の相対的なパフォーマンス向上を示します。

Python インタープリターの機能の拡張には、C++ (または C) で記述されたコード モジュールが一般的に使われます。 拡張モジュールの主要な種類は次の 3 つです。

  • アクセラレータ モジュール: パフォーマンスの高速化を実現します。 Python はインタープリター言語であるため、C++ でアクセラレータ モジュールを記述してパフォーマンスを向上させることができます。
  • ラッパー モジュール: 既存の C/C++ インターフェイスを Python コードに公開するか、Python から簡単に使用できる、より Python に近い API を公開します。
  • 低レベル システム アクセス モジュール: システム アクセス モジュールを作成し、CPython ランタイム、オペレーティング システム、または基になるハードウェアの低レベル機能にアクセスできます。

この記事では、Python で C++ 拡張機能を使用できるようにする 2 つの方法についても説明します。

  • Python のドキュメントで説明されている標準の CPython 拡張機能を使用する。
  • その簡潔さから C++ 11 に推奨される PyBind11 を使用する。 互換性を確保するために、より新しいバージョンの Python を使用していることを確認してください。

このチュートリアルのサンプルの完全版は、GitHub の python-samples-vs-cpp-extension で入手できます。

前提条件

  • Python 開発ワークロードがインストールされた Visual Studio 2017 以降。 ワークロードには Python ネイティブ開発ツールが含まれており、それによりネイティブ拡張機能に必要な C++ ワークロードとツールセットが追加されます。

    Screenshot of a list of Python development options, highlighting the Python native development tools option.

    インストール オプションの詳細については、Visual Studio 用の Python サポートのインストールに関するページを参照してください。

    Note

    データ サイエンスと分析アプリケーション ワークロードをインストールすると、Python と Python ネイティブ開発ツール オプションが既定でインストールされます。

  • Python を別個にインストールする場合は、Python インストーラーの [詳細オプション][デバッグ シンボルのダウンロード] を必ず選択してください。 このオプションは、Python コードとネイティブ コードの間で混合モードのデバッグを使用する場合に必要です。

Python アプリケーションを作成する

Python アプリケーションを作成するには、次の手順を実行します。

  1. Visual Studio で [ファイル]>[新規]>[プロジェクト] の順に選択して、新しい Python プロジェクトを作成します。

  2. [新しいプロジェクトの作成] ダイアログで、「python」を検索します。 [Python アプリケーション] テンプレートを選択し、[次へ] を選択します。

  3. [プロジェクト名][場所] を入力し、[作成] を選びます。

    Visual Studio によって新しいプロジェクトが作成されます。 ソリューション エクスプローラーでプロジェクトが開き、コード エディターでプロジェクト ファイル (.py) が開きます。

  4. 以下のコードを .py ファイルに貼り付けます。 Python の編集機能をいくつか体験する場合は、コードを手動で入力してみてください。

    このコードは、数値演算ライブラリを使用せずに双曲正接を計算します。なお、このコードは Python ネイティブ拡張機能を使用して後で高速化されます。

    ヒント

    C++ で書き換える前に、純粋な Python でコードを記述します。 このようにすることで、ネイティブの Python コードが確実に正しいことをより簡単に確認できます。

    from random import random
    from time import perf_counter
    
    # Change the value of COUNT according to the speed of your computer.
    # The value should enable the benchmark to complete in approximately 2 seconds.
    COUNT = 500000
    DATA = [(random() - 0.5) * 3 for _ in range(COUNT)]
    
    e = 2.7182818284590452353602874713527
    
    def sinh(x):
        return (1 - (e ** (-2 * x))) / (2 * (e ** -x))
    
    def cosh(x):
        return (1 + (e ** (-2 * x))) / (2 * (e ** -x))
    
    def tanh(x):
        tanh_x = sinh(x) / cosh(x)
        return tanh_x
    
    def test(fn, name):
        start = perf_counter()
        result = fn(DATA)
        duration = perf_counter() - start
        print('{} took {:.3f} seconds\n\n'.format(name, duration))
    
        for d in result:
            assert -1 <= d <= 1, " incorrect values"
    
    if __name__ == "__main__":
        print('Running benchmarks with COUNT = {}'.format(COUNT))
    
        test(lambda d: [tanh(x) for x in d], '[tanh(x) for x in d] (Python implementation)')
    
  5. [デバッグ]>[デバッグなしで開始] の順に選択するか、キーボード ショートカット Ctrl + F5 を選択して、プログラムを実行します。

    コマンド ウィンドウが開き、プログラムの出力が表示されます。

  6. 出力のベンチマーク プロセスで報告された時間に注目します。

    このチュートリアルでは、ベンチマーク プロセスは約 2 秒かかります。

  7. 必要に応じて、コード内の COUNT 変数の値を調整し、コンピューター上でベンチマークを約 2 秒で完了できるようにします。

  8. プログラムをもう一度実行し、変更された COUNT 値でベンチマークが約 2 秒で生成されるかを確認します。

ヒント

ベンチマークを実行する場合は、常に [デバッグ]>[デバッグなしで開始] オプションを使用します。 このメソッドは、Visual Studio デバッガー内でコードを実行したときに発生する可能性のあるオーバーヘッドを回避するのに役立ちます。

C++ のコア プロジェクトを作成する

次の手順に従って、superfastcodesuperfastcode2 という 2 つの同一の C++ プロジェクトを作成します。 後で、各プロジェクトで異なる手段を使用して C++ コードを Python に公開します。

  1. ソリューション エクスプローラーで、ソリューション名を右クリックし、[追加]>[新しいプロジェクト] を選択します。

    Visual Studio ソリューションには、Python プロジェクトと C++ プロジェクトの両方を含めることができます (これは Python 開発に Visual Studio を使用する利点の 1 つです)。

  2. [新しいプロジェクトの追加] ダイアログで、[言語] フィルターを [C++] に設定し、[検索] ボックスに「」と入力します。

  3. プロジェクト テンプレートの結果の一覧で、[空のプロジェクト] を選択し、[次へ] を選択します。

  4. [新しいプロジェクトの構成] ダイアログで、次のとおりに [プロジェクト名] を入力します。

    • 最初のプロジェクトの場合は、superfastcode という名前を入力します。
    • 2 番目のプロジェクトの場合は、superfastcode2 という名前を入力します。
  5. [作成] を選択します

必ずこれらの手順を繰り返し、2 つのプロジェクトを作成してください。

ヒント

Visual Studio に Python ネイティブ開発ツールがインストールされている場合は、別の方法を使用できます。 Python 拡張モジュール テンプレートから始めることができ、その場合は、この記事で説明する手順の多くを事前に完了できます。

この記事のチュートリアルでは、空のプロジェクトから開始することで、拡張モジュールを手順に沿ってビルドする方法を示すことができます。 プロセスを理解したら、独自の拡張機能を作成する場合にこの代替テンプレートを使用すると、時間を節約できます。

プロジェクトに C++ ファイルを追加する

次に、各プロジェクトに C++ ファイルを追加します。

  1. ソリューション エクスプローラーで、プロジェクトを展開して [ソース ファイル] ノード右クリックし、[追加]>[新しい項目] を選択します。

  2. ファイル テンプレートの一覧で、[C++ ファイル (.cpp)] を選択します。

  3. ファイルの [名前] に「module.cpp」と入力し、[追加] を選択します。

    重要

    ファイル名に .cpp 拡張子が含まれていることを確認してください。 Visual Studio は、.cpp 拡張子が付いたファイルを検索して、C++ プロジェクトのプロパティ ページの表示を有効にします。

  4. ツール バーの [構成] ドロップダウン メニューを展開し、ターゲット構成の種類を選択します。

    Screenshot that shows how to set the target configuration type for the C++ project in Visual Studio.

    • 64 ビットの Python ランタイムの場合は、x64 構成をアクティブにします。
    • 32 ビットの Python ランタイムの場合は、Win32 構成をアクティブにします。

両方のプロジェクトでこれらの手順を必ず繰り返してください。

プロジェクトのプロパティを構成する

新しい C++ ファイルにコードを追加する前に、C++ モジュール プロジェクトごとにプロパティを構成し、構成をテストしてすべて動作していることを確認します。

各モジュールのデバッグ ビルド構成とリリース ビルド構成の両方について、プロジェクトのプロパティを設定する必要があります。

  1. ソリューション エクスプローラーで、C++ モジュール プロジェクト (superfastcode または superfastcode2) を右クリックし、[プロパティ] を選択します。

  2. モジュールのデバッグ ビルドのプロパティを構成してから、リリース ビルドに同じプロパティを構成します。

    プロジェクトの [プロパティ ページ] ダイアログの上部で、次のファイル構成オプションを構成します。

    Screenshot that shows how to set the project build and platform options for the C++ file in Visual Studio.

    1. [構成] では、[デバッグ] または [リリース] を選択します。 (これらのオプションは、"アクティブ" というプレフィックス付きで表示される場合があります)

    2. [プラットフォーム] では、前の手順で選択した内容に応じて、[アクティブ (x64)] または [アクティブ (Win32)] を選択します。

      Note

      独自のプロジェクトを作成する場合は、特定のシナリオ要件に従って、デバッグ構成とリリース構成を個別に構成する必要があります。 この演習では、CPython のリリース ビルドを使用するように構成を設定します。 この構成により、アサーションを含む、C++ ランタイムの一部のデバッグ機能が無効になります。 CPython デバッグ バイナリ (python_d.exe) を使用する場合は、異なる設定が必要になります。

    3. 次の表で説明に従って、その他のプロジェクトのプロパティを設定します。

      プロパティ値を変更するには、プロパティ フィールドに値を入力します。 一部のフィールドでは、現在の値を選択して選択肢のドロップダウン メニューを展開したり、ダイアログを開いて値を定義したりできます。

      タブの値を更新したら、別のタブに切り替える前に [適用] を選択します。このアクションにより、変更を確実に保持できます。

      タブとセクション プロパティ
      [構成プロパティ]>[全般] ターゲット名 from...import ステートメントで Python からモジュールを参照するときのモジュール名を指定します (superfastcode など)。 この名前は、Python のモジュールを定義するときに C++ コードでも使用します。 プロジェクトの名前をモジュール名として使用するには、既定値の $<プロジェクト名> のままにしておきます。 python_d.exe では、名前の末尾に _d を追加します。
      [構成の種類] ダイナミック ライブラリ (.dll)
      [構成プロパティ]>[詳細] [ターゲット ファイルの拡張子] .pyd (Python 拡張モジュール)
      [C/C++]>[全般] 追加のインクルード ディレクトリ インストールに合わせて Python の include フォルダーを追加します (例: c:\Python36\include)。
      [C/C++]>[プリプロセッサ] プリプロセッサの定義 _DEBUG 値を NDEBUG に変更して、CPython の非デバッグ バージョンに一致させます (存在する場合)。 python_d.exe を使用する場合は、この値を変更せずにそのままにしておきます。
      [C/C++]>[コード生成] ランタイム ライブラリ CPython のリリース (非デバッグ バージョン) に一致する [マルチスレッド DLL (/MD)]python_d.exe を使用する場合は、この値を [マルチスレッド デバッグ DLL (/MDd)] のままにします。
      基本ランタイムのチェック 既定
      [リンカー]>[全般] 追加のライブラリ ディレクトリ インストールに合わせて、 .lib ファイルが含まれる Python の libs フォルダーを追加します (例:c:\Python36\libs)。 .py ファイルが含まれる Lib フォルダー ''ではなく''、必ず、 .lib ファイルが含まれる libs フォルダーをポイントするようにしてください。

      重要

      [C/C++] タブがプロジェクトのプロパティのオプションとして表示されない場合、プロジェクトには、Visual Studio で C/C++ ソース ファイルとして識別されるコード ファイルは含まれません。 このような条件は、 .c または .cpp ファイル拡張子を付けずにソース ファイルを作成すると発生する場合があります。

      C++ ファイルの作成時に module.cpp の代わりに module.coo を誤って入力した場合、Visual Studio でファイルは作成されますが、ファイルの種類は C/C+ コンパイラに設定されません。 このファイルの種類は、[プロジェクトのプロパティ] ダイアログの [C/C++ プロパティ] タブの存在をアクティブ化するために必要です。 .cpp ファイル拡張子でコード ファイルの名前を変更しても、誤った識別は行われます。

      コード ファイルの種類を正しく設定するには、ソリューション エクスプローラーでコード ファイルを右クリックし、[プロパティ] を選択します。 [項目の種類] で、[C/C++ コンパイラ] を選択 します。

    4. すべてのプロパティを更新したら、[OK] を選択します。

    他のビルド構成に対して同じ手順を繰り返します。

  3. 現在の構成をテストします。 両方の C++ プロジェクトのデバッグ ビルドとリリース ビルドの両方について、次の手順を繰り返します。

    1. Visual Studio ツール バーで、[ビルド] 構成を [デバッグ] または [リリース]に設定します。

      Screenshot that shows how to set the build configuration for the C++ project in Visual Studio.

    2. ソリューション エクスプローラーで、C++ project プロジェクトを右クリックし、[ビルド] を選択します。

      .pyd ファイルは、C++ プロジェクト フォルダー自体ではなく、デバッグおよびリリースの下のソーリューション フォルダーにあります。

コードとテスト構成を追加する

これで、C++ ファイルにコードを追加し、リリース ビルドをテストする準備ができました。

  1. superfastcode C++ プロジェクトの場合は、コード エディターで module.cpp ファイルを開きます。

  2. module.cpp ファイルに、次のコードを貼り付けます。

    #include <Windows.h>
    #include <cmath>
    
    const double e = 2.7182818284590452353602874713527;
    
    double sinh_impl(double x) {
        return (1 - pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double cosh_impl(double x) {
        return (1 + pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double tanh_impl(double x) {
        return sinh_impl(x) / cosh_impl(x);
    }
    
  3. 変更を保存します。

  4. C++ プロジェクトの リリース 構成をビルドして、コードが正しいことを確認します。

手順を繰り返して、superfastcode2 プロジェクトの C++ ファイルにコードを追加し、リリース ビルドをテストします。

C++ プロジェクトを Python の拡張機能に変換する

C++ DLL を Python の拡張機能にするには、まず、エクスポートしたメソッドを Python の型とやりとりするように変更します。 次に、モジュールのメソッドの定義と共に、モジュールをエクスポートするための関数を追加します。

以降のセクションでは、CPython 拡張機能と PyBind11 を使用して拡張機能を作成する方法について説明します。 superfasctcode プロジェクトは CPython 拡張機能を使用し、superfasctcode2 プロジェクトは PyBind11 を実装します。

CPython の拡張機能を使用する

このセクションで提示されているコードの詳細については、「Python/C API リファレンス マニュアル」、特に「モジュール オブジェクト」ページを参照してください。 参照コンテンツを確認するときは、右上のドロップダウン リストで Python のバージョンを必ず選択してください。

  1. superfastcode C++ プロジェクトの場合は、コード エディターで module.cpp ファイルを開きます。

  2. Python.h ヘッダー ファイルを含めるために、module.cpp ファイルの先頭にステートメントを追加します。

    #include <Python.h>
    
  3. Python の型 (つまり、PyObject*) を受け入れて戻すように、tanh_impl メソッドを置き換えます。

    PyObject* tanh_impl(PyObject* /* unused module reference */, PyObject* o) {
        double x = PyFloat_AsDouble(o);
        double tanh_x = sinh_impl(x) / cosh_impl(x);
        return PyFloat_FromDouble(tanh_x);
    }
    
  4. ファイルの最後に、C++ tanh_impl 関数を Python に提示する方法を定義する構造体を追加します。

    static PyMethodDef superfastcode_methods[] = {
        // The first property is the name exposed to Python, fast_tanh
        // The second is the C++ function with the implementation
        // METH_O means it takes a single PyObject argument
        { "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr },
    
        // Terminate the array with an object containing nulls
        { nullptr, nullptr, 0, nullptr }
    };
    
  5. Python コードでモジュールを参照する方法を定義する構造体を追加します (具体的には、from...import ステートメントを使用する場合)。

    このコードでインポートされる名前は、[構成プロパティ]>[全般]>[ターゲット名] のプロジェクト プロパティの値と一致する必要があります。

    次の例では、"superfastcode" という名前は Python で from superfastcode import fast_tanh ステートメントを使用できることを意味します。これは、fast_tanhsuperfastcode_methods 内で定義されているためです。 module.cpp など、C++ プロジェクトの内部にあるファイル名は重要ではありません。

    static PyModuleDef superfastcode_module = {
        PyModuleDef_HEAD_INIT,
        "superfastcode",                        // Module name to use with Python import statements
        "Provides some functions, but faster",  // Module description
        0,
        superfastcode_methods                   // Structure that defines the methods of the module
    };
    
  6. Python でモジュールを読み込むときに呼び出すメソッドを追加します。 メソッド名は PyInit_<module-name> になる必要があります。ここで、<module-name> は C++ プロジェクトの [構成プロパティ]>[全般]>[ターゲット名] プロパティと正確に一致します。 つまり、メソッド名は、プロジェクトでビルドされた .pyd ファイルのファイル名と一致します。

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. C++ プロジェクトをビルドして、コードを検証します。 エラーが発生した場合は、「コンパイル エラーのトラブルシューティングを行う」セクションを参照してください。

PyBind11 を使用する

前のセクションの superfastcode プロジェクトに関する手順を実行する場合、C++ CPython 拡張機能のモジュール構造を作成するための定型コードが演習で必要になる場合があります。 この演習を通じて、PyBind11 によってコーディング プロセスが簡略化されることを確認します。 同じ結果を得るために C++ ヘッダー ファイル内のマクロを使用しますが、コードははるかに少なくなります。 ただし、Visual Studio で PyBind11 ライブラリとインクルード ファイルを検索できるようにするには、追加の手順が必要です。 このセクションのコードの詳細については、PyBind11 の基本を参照してください。

PyBind11 をインストールする

最初の手順は、プロジェクト構成に PyBind11 をインストールすることです。 この演習では、[開発者用 PowerShell] ウィンドウを使用します。

  1. [ツール]>[コマンド ライン]>[開発者用 PowerShell] ウィンドウを開きます。

  2. 開発者用 PowerShell ウィンドウで、pip コマンドの pip install pybind11 または py -m pip install pybind11 を使用して PyBind11 をインストールします。

    Visual Studio では、PyBind11 とその依存パッケージがインストールされます。

PyBind11 パスをプロジェクトに追加する

PyBind11 をインストールしたら、プロジェクトの [追加インクルード ディレクトリ] プロパティに PyBind11 パスを追加する必要があります。

  1. [開発者用 PowerShell] ウィンドウで、コマンド python -m pybind11 --includes または py -m pybind11 --includes を実行します。

    この操作により、プロジェクトのプロパティに追加する必要がある PyBind11 パスの一覧が出力されます。

  2. ウィンドウ内のパスの一覧を強調表示し、ウィンドウ ツール バーの [コピー] (ダブル ページ) を選択します。

    Screenshot that shows how to highlight and copy the list of paths from the Developer PowerShell window in Visual Studio.

    連結されたパスの一覧がクリップボードに追加されます。

  3. ソリューション エクスプローラーsuperfastcode2 プロジェクトを右クリックし、 [プロパティ] を選択します。

  4. [プロパティ ページ] ダイアログの上部にある [構成] フィールドで、[リリース] を選択します。 (このオプションは、"アクティブ" というプレフィックス付きで表示される場合があります)

  5. ダイアログの [C/C++]>[全般] タブで、[追加のインクルード ディレクトリ] プロパティのドロップダウン メニューを展開し、[編集] を選択します。

  6. ポップアップ ダイアログで、コピーしたパスの一覧を追加します。

    [開発者用 PowerShell] ウィンドウからコピーした連結リスト内の各パスに対して、次の手順を繰り返します。

    1. ポップアップ ダイアログ ツール バーの [改行] (プラス記号の付いているフォルダー) を選択します。

      Screenshot that shows how to add a PyBind11 path to the Additional Include Directories property.

      Visual Studio では、パスの一覧の上部に空の行が追加され、挿入カーソルが先頭に配置されます。

    2. PyBind11 パスを空の行に貼り付けます。

      [その他のオプション] (...) を選択し、ポップアップ ファイル エクスプローラー ダイアログを使用してパスの場所を参照することもできます。

      重要

      • パスに -I プレフィックスが含まれている場合は、パスからプレフィックスを削除します。
      • Visual Studio でパスを認識するには、パスを別の行に配置する必要があります。

      新しいパスを追加すると、Visual Studio の "評価された値" フィールドに確認済みのパスが表示されます。

  7. [OK] を選択してポップアップ ダイアログを閉じます。

  8. [プロパティ ページ] ダイアログの上部で、[追加のインクルード ディレクトリ] プロパティの値にカーソルを合わせて、PyBind11 パスが存在することを確認します。

  9. [OK] を選択して、プロパティの変更内容を適用します。

module.cpp ファイルを更新する

最後は、PyBind11 ヘッダー ファイルとマクロ コードをプロジェクト C++ ファイルに追加する手順です。

  1. superfastcode2 C++ プロジェクトの場合は、コード エディターで module.cpp ファイルを開きます。

  2. pybind11.h ヘッダー ファイルを含めるために、module.cpp ファイルの先頭にステートメントを追加します。

    #include <pybind11/pybind11.h>
    
  3. module.cpp ファイルの末尾に、C++ 関数へのエントリ ポイントを定義する PYBIND11_MODULE マクロのコードを追加します。

    namespace py = pybind11;
    
    PYBIND11_MODULE(superfastcode2, m) {
        m.def("fast_tanh2", &tanh_impl, R"pbdoc(
            Compute a hyperbolic tangent of a single argument expressed in radians.
        )pbdoc");
    
    #ifdef VERSION_INFO
        m.attr("__version__") = VERSION_INFO;
    #else
        m.attr("__version__") = "dev";
    #endif
    }
    
  4. C++ プロジェクトをビルドして、コードを検証します。 エラーが発生した場合は、次のセクション、「コンパイル エラーのトラブルシューティングを行う」を参照してください。

コンパイル エラーのトラブルシューティングを行う

C++ モジュールのビルドが失敗する原因になる可能性のある問題については、以降のセクションを確認してください。

エラー: ヘッダー ファイルが見つかりません

Visual Studio は、「E1696: ソース ファイル "Python.h" を開くことができません」または「C1083: インクルード ファイルを開くことができません: "Python.h": そのようなファイルまたはディレクトリはありません」のようなエラー メッセージを返します。

このエラーは、コンパイラがプロジェクトに必要なヘッダー (.h) ファイルを見つけることができないことを示します。

  • superfastcode プロジェクトの場合は、[C/C++]>[全般]>[追加のインクルード ディレクトリ] プロジェクト プロパティに、Python のインストール用の [インクルード] フォルダーへのパスが含まれていることを確認します。 「プロジェクトのプロパティを構成する」の手順を確認します。

  • superfastcode2 プロジェクトの場合は、同じプロジェクト プロパティに PyBind11 のインストール用の [インクルード] フォルダーへのパスが含まれていることを確認します。 「PyBind パスをプロジェクトに追加する」の手順を確認します。

Python のインストール構成情報へのアクセスの詳細については、Python ドキュメントを参照してください。

エラー: Python ライブラリが見つかりません

Visual Studio は、コンパイラがプロジェクトに必要なライブラリ (DLL) ファイルを見つけることができないことを示すエラーを返します。

  • C++ プロジェクト (superfastcode または superfastcode2) の場合は、[リンカー]>[全般]>[追加のライブラリ ディレクトリ] プロパティに、Python のインストール用の libs フォルダーへのパスが含まれていることを確認します。 「プロジェクトのプロパティを構成する」の手順を確認します。

Python のインストール構成情報へのアクセスの詳細については、Python ドキュメントを参照してください。

Visual Studio は、x64 や Win32 など、プロジェクトのターゲット アーキテクチャ構成に関連するリンカー エラーを報告します。

  • C++ プロジェクト (superfastcode または superfastcode2) の場合は、Python のインストールに合うようターゲット構成を変更します。 たとえば、C++ プロジェクトのターゲット構成が Win32 で、Python のインストールが 64 ビットの場合は、C++ プロジェクトのターゲット構成を x64 に変更します。

コードをテストして結果を比較する

これで、Python 拡張機能の構造の DLL ができたので、Python プロジェクトからそれらを参照し、モジュールをインポートして、それらのメソッドを使うことができます。

Python で DLL を使用できるようする

DLL を Python で使用できるようにする方法がいくつかあります。 ここで考えられるオプションは次の 2 つです。

Python プロジェクトと C++ プロジェクトが同じソリューション内にある場合は、次のアプローチを取ることができます。

  1. ソリューション エクスプローラーで、Python プロジェクト内の [参照設定] ノードを右クリックし、[参照の追加] をクリックします。

    このアクションは、C++ プロジェクトではなく、Python プロジェクトに対して必ず実行してください。

  2. [参照の追加] ダイアログの [プロジェクト] タブを展開します。

  3. superfastcode プロジェクトと superfastcode2 プロジェクトの両方のチェックボックスを選択し、[OK] を選択します。

    Screenshot that shows how to add a reference to the super fast code project in Visual Studio.

もう 1 つのアプローチは、Python 環境に C++ 拡張機能モジュールをインストールすることです。 このメソッドを使用すると、他の Python プロジェクトでモジュールを使用できるようになります。 詳細については、setuptools プロジェクトのドキュメントを参照してください。

Python 環境に C++ 拡張モジュールをインストールするには、次の手順を実行します。

  1. ソリューション エクスプローラーで、C++ プロジェクト ノードを右クリックし、[追加]>[新しい項目] の順に選択します。

  2. ファイル テンプレートの一覧で、[C++ ファイル (.cpp)] を選択します。

  3. ファイルの [名前] に「setup.py」と入力してから、[追加] を選択します。

    Python (.py) 拡張子のファイル名を必ず入力してください。 Visual Studio は、C++ ファイル テンプレートを使用しているにもかかわらず、そのファイルを Python コードとして認識します。

    Visual Studio のコード エディターで新しいファイルが開きます。

  4. 次のコードを新しいファイルに貼り付けます。 拡張メソッドに対応するコード バージョンを選択します。

    • CPython の拡張機能 (superfastcode プロジェクト):

      from setuptools import setup, Extension
      
      sfc_module = Extension('superfastcode', sources = ['module.cpp'])
      
      setup(
          name='superfastcode',
          version='1.0',
          description='Python Package with superfastcode C++ extension',
          ext_modules=[sfc_module]
      )
      
    • PyBind11 (superfastcode2 プロジェクト):

      from setuptools import setup, Extension
      import pybind11
      
      cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7']
      
      sfc_module = Extension(
          'superfastcode2',
          sources=['module.cpp'],
          include_dirs=[pybind11.get_include()],
          language='c++',
          extra_compile_args=cpp_args,
      )
      
      setup(
          name='superfastcode2',
          version='1.0',
          description='Python package with superfastcode2 C++ extension (PyBind11)',
          ext_modules=[sfc_module],
      )
      
  5. C++ プロジェクトで、pyproject.toml という名前の 2 つ目のファイルを作成し、次のコードを貼り付けます。

    [build-system]
    requires = ["setuptools", "wheel", "pybind11"]
    build-backend = "setuptools.build_meta"
    

    TOML (.toml) ファイルは、構成ファイルに Tom の明らかな最小言語形式を使用します。

  6. 拡張機能をビルドするには、コード ウィンドウのタブで pyproject.toml というファイル名を右クリックし、[完全なパスのコピー] を選択します。

    Screenshot that shows how to copy the full path to the py project toml file in Visual Studio.

    pyproject.toml 名は、使用する前にパスから削除します。

  7. ソリューション エクスプローラーで、ソリューションの [Python環境] ノードを展開します。

  8. アクティブな Python 環境 (太字で表示) を右クリックし、[Python パッケージの管理] を選択します。

    [Python 環境] ペインが開きます。

    必要なパッケージが既にインストールされている場合は、このペインに一覧表示されます。

    • 続行する前に、パッケージ名の横にある X を選択してアンインストールします。

    Screenshot that shows how to uninstall a package in the Python Environments pane.

  9. [Python 環境] ペインの検索ボックスにコピーしたパスを貼り付け、パスの末尾から pyproject.toml ファイル名を削除します。

    Screenshot that shows how to enter the path in the Python Environments pane to install the extension module.

  10. Enter キーを選択して、コピーしたパスの場所からモジュールをインストールします。

    ヒント

    アクセス許可エラーによりインストールが失敗した場合は、--user 引数をコマンドの末尾に追加し、コマンドをもう一度試します。

Python から DLL を呼び出す

前のセクションの説明に従って DLL を Python で使用できるようにしたら、Python から superfastcode.fast_tanh 関数と superfastcode2.fast_tanh2 関数を呼び出す準備が整います。 その後、関数のパフォーマンスを Python の実装と比較できます。

Python から拡張モジュール DLL を呼び出すには、次の手順に従います。

  1. コード エディターで Python プロジェクト用の .py ファイルを開きます。

  2. ファイルの末尾に次のコードを追加して、DLL からエクスポートされたメソッドを呼び出し、その出力を表示します。

    from superfastcode import fast_tanh
    test(lambda d: [fast_tanh(x) for x in d], '[fast_tanh(x) for x in d] (CPython C++ extension)')
    
    from superfastcode2 import fast_tanh2
    test(lambda d: [fast_tanh2(x) for x in d], '[fast_tanh2(x) for x in d] (PyBind11 C++ extension)')
    
  3. [デバッグ]>[デバッグなしで開始] の順に選択するか、キーボード ショートカット Ctrl+F5 を使用して、Python プログラムを実行します。

    Note

    [デバッグなしで開始] コマンドが使用できない場合は、ソリューション エクスプローラーで Python プロジェクトを右クリックしてから、[スタートアップ プロジェクトに設定] を選択します。

    プログラムが実行されると、C++ ルーチンの実行速度が Python 実装の約 5 から 20 倍速くなります。

    一般的なプログラム出力の例を次に示します。

    Running benchmarks with COUNT = 500000
    [tanh(x) for x in d] (Python implementation) took 0.758 seconds
    
    [fast_tanh(x) for x in d] (CPython C++ extension) took 0.076 seconds
    
    [fast_tanh2(x) for x in d] (PyBind11 C++ extension) took 0.204 seconds
    
  4. 時間の違いがより顕著になるように COUNT 変数を増やしてみてください。

    また、C++ モジュールのデバッグ ビルドの実行は、リリース ビルドよりも低速になります。これは、デバッグ ビルドが十分に最適化されず、さまざまなエラー チェックが含まれるためです。 ビルド構成間で切り替えを試して比較してみてください。ただし、リリース構成に対して前に設定したプロパティを更新するようにしてください。

プロセスの速度とオーバーヘッドに対処する

出力時に、PyBind11 拡張機能の速度が CPython 拡張機能ほど速くないことに気付く場合があるかもしれません (ただ、純粋な Python 実装に比べたら速いはずです)。 この違いの主な理由は、METH_O フラグの使用です。 このフラグは、複数のパラメーター、パラメーター名、またはキーワード引数をサポートしていません。 PyBind11 では、より Python に類似したインターフェイスを呼び出し元に提供するために、少し複雑なコードを生成します。 テスト コードで関数が 50 万回呼び出されるため、結果的にオーバーヘッドが大幅に増加する可能性があります。

for ループをネイティブの Python コードに移動することで、オーバーヘッドをさらに軽減できます。 この方法では、反復子プロトコル (または関数パラメーター用の PyBind11 の py::iterable 型) を使用して、各要素を処理する必要があります。 Python と C++ の間で繰り返される切り替えを削除することは、シーケンスの処理にかかる時間を短縮する効果的な方法です。

インポート エラーのトラブルシューティングを行う

モジュールをインポートしようとしたときに ImportError メッセージを受け取った場合は、次のいずれかの方法で解決できます。

  • プロジェクト参照を使用してビルドする場合は、C++ プロジェクトのプロパティが、Python プロジェクトでアクティブ化された Python 環境と一致していることを確認してください。 インクルード (.h) ファイルとライブラリ (DLL) ファイルで同じフォルダーの場所が使用されていることを確認します。

  • 出力ファイルの名前が正しいことを確認します (superfastcode.pyd など)。 名前や拡張子が正しくないと、必要なファイルをインポートできません。

  • setup.py ファイルを使用してモジュールをインストールする場合は、Python プロジェクト用にアクティブ化された Python 環境で pip コマンドを必ず実行してください。 ソリューション エクスプローラーでプロジェクトのアクティブな Python 環境を展開すると、superfastcode などの C++ プロジェクトのエントリが表示されます。

C++ コードをデバッグする

Visual Studio では、Python と C++ コードを一緒にデバッグすることをサポートしています。 以降の手順で、superfastcode C++ プロジェクトのデバッグ プロセスについて説明しますが、superfastcode2 プロジェクトのプロセスは同じです。

  1. ソリューション エクスプローラーで Python プロジェクトを右クリックし、[プロパティ] を選びます。

  2. [プロパティ] ペインで、[デバッグ] タブを選択し、[デバッグ]>[ネイティブ コードのデバッグを有効にする] オプションを選択します。

    ヒント

    ネイティブ コードのデバッグを有効にすると、一時停止して [続行するには、任意のキーを押してください] プロンプトが表示されることなく、プログラムが終了するとすぐに、Python の出力ウィンドウが閉じることがあります。 ネイティブ コードのデバッグを有効にした後に強制的に一時停止とプロンプト表示を実行するには、[デバッグ] タブの [実行]>[インタープリター引数] フィールドに -i 引数を追加します。この引数によって、Python インタープリターはコードの実行後に対話モードになります。 プログラムは、ユーザーが Ctrl+Z+Enter キーを選択してウィンドウを閉じるのを待ちます。 別のアプローチとしては、Python プログラムの最後に import os ステートメントと os.system("pause") ステートメントを追加する方法があります。 このコードで、元の一時停止プロンプトが複製されます。

  3. [ファイル]>[保存] (または Ctrl+S) を選択して、プロパティの変更内容を保存します。

  4. Visual Studio のツール バーで、ビルド構成を [デバッグ] に設定します。

  5. 通常、デバッガーでコードを実行するとより時間がかかるため、Python プロジェクト .py ファイルの COUNT 変数を、既定値の約 5 分の 1 の値に変更するとよいかもしれません。 たとえば、500000 から 100000 に変更します。

  6. C++ コードで、tanh_impl メソッドの最初のコード行にブレークポイントを設定します。

  7. [デバッグ]>[デバッグの開始] を選択してデバッガーを起動するか、キーボード ショートカット F5 を使用します。

    ブレークポイント コードが呼び出されるとデバッガーが停止します。 ブレークポイントがヒットしない場合は、構成が確実に [デバッグ] に設定されていることと、プロジェクトを保存していること (これはデバッガーの開始時に自動的に行われません) を確認します。

    Screenshot of C++ code that contains a breakpoint in Visual Studio.

  8. ブレークポイントで、C++ コードをステップ実行したり、変数を調べたりすることができます。 これらの機能の詳細については、「Python と C++ を同時にデバッグする」を参照してください。

他の方法

次の表で説明されているように、さまざまな方法で Python の拡張機能を作成することができます。 最初の 2 行 (CPythonPyBind11) については、この記事で説明されています。

アプローチ Vintage 代表的ユーザー
CPython 用の C/C++ 拡張モジュール 1991 標準ライブラリ
PyBind11 (C++ 向けに推奨) 2015
Cython (C 向けに推奨) 2007 geventkivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 cryptographypypy
SWIG 1996 crfsuite
Boost.Python 2002
cppyy 2017