Suffix Array をソートする

標準ドキュメントの C API リファレンスを見ながら作った、はじめての C による Python モジュール。
Sary の sary_builder_sort() をラップしただけの単機能です。Python で mksary コマンドで生成されるものと少し異なる (具体的には行頭の数バイトをスキップした) Suffix Array を構築した後、仕上げにソートするために作りました。
禁句:os.popen('mksary -s hoge.txt')
C のソースを PHP の拡張モジュールを作るときとほとんど同じような感じで書けたのは嬉しい誤算でした。
zval/PyObject, zend_parse_parameters()/PyArg_ParseTuple(), RETURN_TRUE/Py_RETURN_TRUE のように、型・関数・マクロの名前を置き換えるだけでそのまま使えそうな勢いです。

#include <Python.h>
#include <sary.h>
#include <glib.h>

static PyObject *my_sary_builder_sort(PyObject *self, PyObject *args);
static PyObject *SaryError;

static PyMethodDef SaryMethods[] = {
	{"sort", my_sary_builder_sort, METH_VARARGS, "Sort a suffix array."},
	{NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initsary(void)
{
	PyObject *m;
	
	m = Py_InitModule("sary", SaryMethods);
	
	SaryError = PyErr_NewException("sary.error", NULL, NULL);
	Py_INCREF(SaryError);
	PyModule_AddObject(m, "error", SaryError);
}

static PyObject *my_sary_builder_sort(PyObject *self, PyObject *args)
{
	const char *file_name;
	SaryBuilder *builder; 
	gboolean result;
	
	if (!PyArg_ParseTuple(args, "s", &file_name)) {
		return NULL;
	}
	
	builder = sary_builder_new((const gchar*)file_name);
	if (!builder) {
		Py_RETURN_FALSE;
	}
	
	result = sary_builder_sort(builder);
	sary_builder_destroy(builder);
	if (!result) {
		Py_RETURN_FALSE;
	}
	Py_RETURN_TRUE;
}


setup.py はこんな感じ。*.pc (あるいは *-config スクリプト) が用意されているライブラリだと設定が楽で良いですね。(Windows ではどうなるんだろう?)
これが PHP だと config.m4 と睨めっこしながらうまくビルドできるまで phpize; configure; make; make distclean; phpize --clean を繰り返す羽目になるので、distutil は素晴らしいなあと。(PHP でも CodeGen_PECL を使えばだいぶ楽になりますが、その話はまた後日)

#!python

from distutils.core import setup, Extension
import os, re

def get_include_dirs(name):
    result = os.popen('pkg-config --cflags-only-I ' + name).read()
    return re.findall(r'-I(\S+)', result)

def get_libraries(name):
    result = os.popen('pkg-config --libs-only-l ' + name).read()
    return re.findall(r'-l(\S+)', result)

def get_library_dirs(name):
    result = os.popen('pkg-config --libs-only-L ' + name).read()
    return re.findall(r'-L(\S+)', result)

module1 = Extension('sary',
        include_dirs = get_include_dirs('sary'),
        libraries = get_libraries('sary'),
        library_dirs = get_library_dirs('sary'),
        sources = ['sary.c'])

setup(name = 'sary',
        version = '0.0.1',
        description = 'Sary Python binding',
        ext_modules = [module1])


今日もまた眠いため、予告していた PHP 版はまた明日。
僕は C については素人に産毛が生えたぐらいの知識しか持っておらず、Python C API に至っては文書を流し読みしただけなので拙い部分もあると思いますが、平にご容赦を。ツッコミ大歓迎です。