LoginSignup
6
5

More than 3 years have passed since last update.

Pythonを爆速にする「Cython」チュートリアル: C++のコードがライブラリに依存しているとき。準備編

Last updated at Posted at 2020-03-16

概要

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とできるようになります。

setup.py
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から呼べるようにしよう、ということです。

TestClass1.h
namespace my_library
{
class TestClass1{
    public:
        TestClass1();
        void test_function1();
};    
} // namespace my_library
TestClass1.cpp
#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ファイルのようなものです。

my_library.pxd
cdef extern from "../cpp_library/TestClass1.h" namespace "my_library":
    cdef cppclass TestClass1:
        TestClass1()
        void test_function1()
my_library.pyx
import cython
cimport cython

include "test_class1.pyx"

def test():
    return TestClass1Cython()

test_class1.pxd
cdef extern from "../cpp_library/TestClass1.h" namespace "my_library":
    cdef cppclass TestClass1:
        TestClass1()
        void test_function1()
test_class1.pyx
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を書きたい。
そのための準備を整えた。

おわり。

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5