Cythonを使ってみる
python-simplexqueryでは、C&C++でpythonのextensionを書いたけど、これ以上ベタでpython層変換を書くのはきついので、このあたりをサポートするツールを試すことにし、まずcythonを使ってみました。
cythonはpyxというpython言語の拡張言語(pyrex)のソースファイルを、cファイルに変換するpythonモジュールです。distutilsのコマンド拡張も入っているので、setup.pyに.cや.cpp同様直接.pyxを混ぜて入れることができ、cコードに変換後にコンパイルさせることも可能です。
setup.py
from distutils.core import setup, Extension from Cython.Distutils import build_ext setup( name = 'hello', cmdclass = {'build_ext': build_ext}, ext_modules = [Extension("hello", ["chello.c", "hello.pyx"])] )
ミソは、build_extをCythonのものに指定すること
chello.c
#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <string.h> extern void * (* get_malloc(void))(size_t); extern char * say_hello(const char * name) { char * hello = "Hello"; int hellolen = strlen(hello); int namelen = strlen(name); int buflen = hellolen + 1 + namelen + 1; char * buf = get_malloc()(buflen); sprintf(buf, "%s %s", hello, name); return buf; }
mallocはpythonのものを使うことにします。ここでは、それを外で定義させるため、関数get_malloc()経由で取得させました。最初のdefineはcl.exeの警告を消すためのおまじない。
hello.pyx
cdef extern from "Python.h": void * PyMem_Malloc(size_t) void PyMem_Free(void *) pass #cdef extern char * say_hello(char *) # or cdef extern: char * say_hello(char *) pass ctypedef void * malloc_t(size_t) cdef extern malloc_t * get_malloc(): return PyMem_Malloc # function returned func cdef requires arg name of its func type cdef extern void (* get_free())(void * ptr): return &PyMem_Free def say_hello_to(name): # convert py to c # cannot mix py exp in cdef exp without pyvars bname = name.encode("utf-8") cdef char * cname = bname cdef char * buf = say_hello(cname) # convert c to py ret = buf PyMem_Free(buf) return ret.decode("utf-8")
いくつかポイント
実行
普通のと同じで、python setup.py build でビルドできます。できたモジュールはcythonライブラリを必要としません。
python3対応
cythonの生成cファイルは、python2でもpython3でも対応している。
ただし、Cython-0.12.1のCython/Compiler/Main.pyがpython3非互換なのでsetup.py build時に修正する必要がある。
- build時のエラー部分をprint(msg)、raise Excep(msg)、except Excep as ex: などにする程度。2to3でいける?