1.Python/C APIで作ったモジュールはただのdllファイルの拡張子をpydに変更したもの
2.Pythonへdll内にある関数一覧を公開するため、dll内にPyInit_pydファイル名 という関数を用意しておく
3.dll側の関数は戻り値がPyObject*,引数も全てPyObject*型にする
以下のスクリプトを実行し、'INCLUDEPY' という値を見つける。
# where-is-pyh.py import sysconfig import pprint pprint.pprint(sysconfig.get_config_vars())
以下のプログラムを書き、DLLとしてビルドする。
注意:モジュール名==dllファイル名 なので、出力ファイル名が DLLforPython.pyd となるようにプロジェクト設定を調整しておく。普通プロジェクト名がそのままファイル名になるので拡張子設定だけを変えればいい。
// mymod.hpp
// このプロジェクトのビルド結果は「DLLforPython.pyd」となる。
// 当然だが.dllで出力して拡張子だけ変えてもいい
#pragma once #include <Python.h> // libファイルが必要 // anaconda3\Include にPython.hがある時、おそらく // anaconda3\libs にある。 #pragma comment (lib,"python39.lib") // この関数をPython側から呼び出したいとする。 __declspec(dllexport) extern "C" PyObject * hello_in_c(PyObject*);
// ↑今回はlibでimportしないので__declspecの切り替えは不要 // 第一引数はPython側のself用。
// 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "hello_on_python", (PyCFunction)hello_in_c, METH_VARARGS , nullptr }, // ... 公開したい関数の個数分だけ記述する
// 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
// モジュールを定義する構造体 static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "DLLforPython", // モジュール名 "Example of pyhton wrapper", // モジュール説明 0, method_list // Structure that defines the methods of the module };
// 関数名はPyInit_ + pydファイル名 PyMODINIT_FUNC PyInit_DLLforPython() { return PyModule_Create(&module_def); }
// mymod.cpp #include "mymod.hpp" PyObject* hello_in_c(PyObject*) { printf("hello world from C"); return Py_None; }
dllmain.cppはVC++でプロジェクトを作ると勝手にできるのでいじらなければそれでいい。
// dllmain.cpp : DLL アプリケーションのエントリ ポイントを定義します。 #include "pch.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
ビルドしたpydファイルをpythonスクリプトと同じディレクトリに置き、テスト用のスクリプトでimportして使用する。
# test.py import DLLforPython DLLforPython.hello_on_python()