概要
C++ で書いているコードを、どうしてもPythonで呼べる形のライブラリにしたい。
ただ、そこにはCythonという大きな壁が立ちはだかっていた。
これは、Pythonを爆速にする「Cython」チュートリアル: 基本的な構成編 の続きになります。
今回は、cythonizeしたいC++のコードが、何らかのC++ライブラリに依存している時、どうやってcythonizeすればいいか、というところを解説します。今回は準備編です。
コードは「ここのgithub」にあげてあるので、ぜひご覧ください。
そのためには、setup.py
の編集が必要ですが、そのときに一度Cythonから離れて、CMakeLists.txtをどう書けばそもそもC++のコンパイルが通るのか、を理解すると楽になります。
状況説明
以前のフォルダ構成から、名前だけ少し変えて整えました。
user@~/Documents/cython_practice> tree .
.
├── cpp_library
│ ├── TestClass1.cpp
│ └── TestClass1.h
├── cython
│ ├── my_library.pyx
│ ├── test_class1.pxd
│ └── test_class1.pyx
└── setup.py
2 directories, 6 files
おさらいから始めるとすると、
setup.py
はC++, cythonのコードをコンパイルして、ライブラリ用のオブジェクトファイルを作るためのファイルです。これを実行することで、pythonからimport my_libarary
とできるようになります。
from setuptools import setup, Extension, find_packages
from Cython.Build import cythonize
from Cython.Distutils import build_ext
from distutils import sysconfig
ext_modules = [
Extension(
"my_library", sources=[
"./cython/my_library.pyx",
"./cpp_library/TestClass1.cpp"
],
language="c++"
)
]
setup(
name = "my_library",
cmdclass = {"build_ext": build_ext},
ext_modules= cythonize(ext_modules)
)
cpp_library
フォルダの中には、自分のC++で書いたライブラリが入っています。目的は、このTestClass1.cpp, TestClass1.h
をCython化することで、 Pythonから呼べるようにしよう、ということです。
namespace my_library
{
class TestClass1{
public:
TestClass1();
void test_function1();
};
} // namespace my_library
#include <iostream>
#include "TestClass1.h"
using namespace std;
namespace my_library{
TestClass1::TestClass1(){};
void TestClass1::test_function1(){
cout << "printed from cpp function" << endl;
}
}
cython
フォルダの中には、先ほどのC++で書かれたライブラリをCython化するためのコードが格納されています。pxd
ファイルはC++のヘッダーファイル、pyx
はcppファイルのようなものです。
cdef extern from "../cpp_library/TestClass1.h" namespace "my_library":
cdef cppclass TestClass1:
TestClass1()
void test_function1()
import cython
cimport cython
include "test_class1.pyx"
def test():
return TestClass1Cython()
cdef extern from "../cpp_library/TestClass1.h" namespace "my_library":
cdef cppclass TestClass1:
TestClass1()
void test_function1()
import cython
cimport cython
cdef class TestClass1Cython:
cdef TestClass1* ptr
def __cinit__(self):
self.ptr = new TestClass1()
def __deadaloc(self):
del self.ptr
def test_function1_cython(self):
self.ptr.test_function1()
以上を準備したら、コンパイル
python setup.py install
します。実際に、
(myenv) user@~/Documents/cython_practice> python
Python 3.6.7 (default, Nov 16 2019, 21:57:19)
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import my_library
>>> test = my_library.test()
>>> test.test_function1_cython()
printed from cpp function
とでき、cythonize ができていることを確認しましょう。
###cpp_libraryは、あるC++ライブラリに依存している。
という状況で、どうcythonizeするようなsetup.pyを書けば良いか、という問題にぶち当たるかと思います。
今回は、例としてC++で書かれたプログラムが、gmpライブラリに依存しているとします。
gmpライブラリは、
wget https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz &&\
tar xvf gmp-6.1.2.tar.xz &&\
cd gmp-6.1.2 &&\
./configure --prefix=/usr/local/gmp/6_1_2 &&\
make && make check && make install
でコンパイルできます。
今回はここまでで準備が整ったということで、次に
- 実際にgmpをつかって関数を実装、
- CMakeをつかってコンパイル
- cythonizeのためのsetup.py準備
していきたいと思います。
まとめ
cythonを使ってC++をpythonから呼べるライブラリにしたい時、C++側がライブラリに依存している状況で適切なsetup.pyを書きたい。
そのための準備を整えた。
おわり。