サーチ…


DLLのラッピング:CythonからPythonへのC ++

これは、C ++ dllをCythonでラップする例を示しています。それは次の主要なステップをカバーします:

  • Visual Studioを使用してC ++でサンプルDLLを作成します。
  • CythonでDLLをラップすると、Pythonで呼び出すことができます。

Cythonがインストールされていて、それをPythonで正常にインポートできると仮定しています。

DLLの手順については、Visual StudioでDLLを作成することに精通していることも前提としています。

完全な例には、次のファイルの作成が含まれます。

  1. complexFunLib.h :C ++ DLLソースのヘッダーファイル
  2. complexFunLib.cpp :C ++ DLLソースのCPPファイル
  3. ccomplexFunLib.pxd :Cythonの "ヘッダー"ファイル
  4. complexFunLib.pyx :Cythonの "ラッパー"ファイル
  5. setup.py complexFunLib.pydを作成するためのPythonセットアップファイル
  6. run.py :コンパイル済みのCythonラップDLLをインポートするPythonファイルの例

C ++ DLLソース: complexFunLib.hおよびcomplexFunLib.cpp

すでにDLLとヘッダソースファイルがある場合は、これをスキップしてください。最初に、Visual Studioを使用してDLLをコンパイルするC ++ソースを作成します。この場合、複雑な指数関数を使用して高速配列計算を行いたいと考えています。次の2つの関数は、結果がres格納されるkee配列k k*exp(ee)の計算を実行します。単精度と倍精度の両方に対応する2つの関数があります。これらのサンプル関数はOpenMPを使用しているので、OpenMPがプロジェクトのVisual Studioオプションで有効になっていることを確認してください。

Hファイル

// Avoids C++ name mangling with extern "C"
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)  
#include <complex>
#include <stdlib.h>

// Handles 64 bit complex numbers, i.e. two 32 bit (4 byte) floating point numbers
EXTERN_DLL_EXPORT void mp_mlt_exp_c4(std::complex<float>* k, 
                                     std::complex<float>* ee,
                                     int sz, 
                                     std::complex<float>* res, 
                                     int threads);

// Handles 128 bit complex numbers, i.e. two 64 bit (8 byte) floating point numbers
EXTERN_DLL_EXPORT void mp_mlt_exp_c8(std::complex<double>* k,                                       std::complex<double>* ee,
                                     int sz, 
                                     std::complex<double>* res, 
                                     int threads);

CPPファイル

#include "stdafx.h"
#include <stdio.h>
#include <omp.h>
#include "complexFunLib.h"

void mp_mlt_exp_c4(std::complex<float>* k,
                   std::complex<float>* ee,
                   int sz,
                   std::complex<float>* res,
                   int threads)
{
    // Use Open MP parallel directive for multiprocessing
    #pragma omp parallel num_threads(threads)
    {
        #pragma omp for
        for (int i = 0; i < sz; i++) res[i] = k[i] * exp(ee[i]);
    }
}

void mp_mlt_exp_c8(std::complex<double>* k,
                   std::complex<double>* ee,
                   int sz, std::complex<double>* res,
                   int threads)
{
    // Use Open MP parallel directive for multiprocessing
    #pragma omp parallel num_threads(threads)
    {
        #pragma omp for
        for (int i = 0; i < sz; i++) res[i] = k[i] * exp(ee[i]);
    }
}

Cythonソース: ccomplexFunLib.pxdおよびcomplexFunLib.pyx

次に、C ++ DLLをラップするのに必要なCythonソースファイルを作成します。このステップでは、次の前提を設定します。

  • あなたはCythonをインストールしました
  • 上で説明したような、動作中のDLLを所有している

最終的な目標は、PythonモジュールとしてインポートしてC ++で書かれた関数を公開する.pydファイルをコンパイルするために、これらのCythonソースファイルを元のDLL .pyd使用することです。

PXDファイル

このファイルは、C ++ヘッダーファイルに対応しています。ほとんどの場合、小さなCython固有の変更でこのファイルにヘッダーをコピー・ペーストすることができます。この場合、特定のCython複合型が使用されました。 ccomplexFunLib.pxdの先頭にcを追加することに注意してください。これは必須ではありませんが、このような命名規則が組織の維持に役立つことがわかりました。

cdef extern from "complexFunLib.h":
    void mp_mlt_exp_c4(float complex* k, float complex* ee, int sz,
                       float complex* res, int threads);
    void mp_mlt_exp_c8(double complex* k, double complex* ee, int sz,
                       double complex* res, int threads);

PYXファイル

このファイルは、C ++のcppソースファイルに対応しています。この例では、Numpy ndarrayオブジェクトへのポインタをインポートDLL関数にndarrayます。組み込みのCython memoryviewオブジェクトを配列に使用することもできますが、そのパフォーマンスはndarrayオブジェクトほど良くないかもしれません(ただし、構文はかなりクリーンです)。

cimport ccomplexFunLib  # Import the pxd "header"
# Note for Numpy imports, the C import most come AFTER the Python import
import numpy as np  # Import the Python Numpy
cimport numpy as np  # Import the C Numpy

# Import some functionality from Python and the C stdlib
from cpython.pycapsule cimport *

# Python wrapper functions.
# Note that types can be delcared in the signature

def mp_exp_c4(np.ndarray[np.complex64_t, ndim=1] k,
              np.ndarray[np.complex64_t, ndim=1] ee,
              int sz,
              np.ndarray[np.complex64_t, ndim=1] res,
              int threads):
    '''
    TODO: Python docstring
    '''
    # Call the imported DLL functions on the parameters.
    # Notice that we are passing a pointer to the first element in each array
    ccomplexFunLib.mp_mlt_exp_c4(&k[0], &ee[0], sz, &res[0], threads)
    
def mp_exp_c8(np.ndarray[np.complex128_t, ndim=1] k,
              np.ndarray[np.complex128_t, ndim=1] ee,
              int sz,
              np.ndarray[np.complex128_t, ndim=1] res,
              int threads):
    '''
    TODO: Python docstring
    '''
    ccomplexFunLib.mp_mlt_exp_c8(&k[0], &ee[0], sz, &res[0], threads)

Pythonソース: setup.pyrun.py

setup.py

このファイルは、Cythonのコンパイルを実行するPythonファイルです。その目的は、コンパイルされた.pydファイルを生成して、Pythonモジュールによってインポートすることです。この例では、 setup.pyと同じディレクトリに必要なすべてのファイル( complexFunLib.hcomplexFunLib.dllccomplexFunLib.pxd 、およびcomplexFunLib.pyx )をcomplexFunLib.pyxしています。

このファイルが作成されたら、コマンドラインからパラメータ付きで実行する必要があります: build_ext --inplace

このファイルが実行されると、エラーを発生させることなく.pydファイルが生成されます。場合によっては、間違いがあると、 .pydが作成される可能性がありますが無効であることに注意してください。生成された.pydを使用する前に、 setup.pyの実行中にエラーがスローされていないことを確認してください。

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np

ext_modules = [
    Extension('complexFunLib',
              ['complexFunLib.pyx'],
              # Note here that the C++ language was specified
              # The default language is C
              language="c++",  
              libraries=['complexFunLib'],
              library_dirs=['.'])
    ]

setup(
    name = 'complexFunLib',
    cmdclass = {'build_ext': build_ext},
    ext_modules = ext_modules,
    include_dirs=[np.get_include()]  # This gets all the required Numpy core files
)

run.py

complexFunLib Pythonモジュールに直接インポートし、ラップされたDLL関数を呼び出すことができます。

import complexFunLib
import numpy as np

# Create arrays of non-trivial complex numbers to be exponentiated,
# i.e. res = k*exp(ee)
k = np.ones(int(2.5e5), dtype='complex64')*1.1234 + np.complex64(1.1234j)
ee = np.ones(int(2.5e5), dtype='complex64')*1.1234 + np.complex64(1.1234j) 
sz = k.size  # Get size integer
res = np.zeros(int(2.5e5), dtype='complex64')  # Create array for results

# Call function
complexFunLib.mp_exp_c4(k, ee, sz, res, 8)  

# Print results
print(res)


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow