らんだむな記憶

blogというものを体験してみようか!的なー

pythonでC言語拡張モジュール

を作ろうかなとふと思った。
ggると、

#include "Python.h"

らしい。なるほど!

g++  -c main.cpp
main.cpp:1:20: fatal error: Python.h: そのようなファイルやディレクトリはありません
 #include "Python.h"
                    ^

想定内!(゜∀ ゜)

gcc - fatal error: Python.h: No such file or directory - Stack Overflowを参考に、Ubuntu14.04LTSにて

$ sudo apt-get install python-dev

よし!まだエラーが出るぞ!さきほどの海外フォーラムをもっと読んでみる。

# find /usr/include/python2.7 -name "Python.h"
/usr/include/python2.7/Python.h

うん。これだゎ。incude pathsを通せばコンパイルが通るようになった。さてこっから面倒くさいので先人の知恵に頼るということで、親切なページをggる。
http://qiita.com/mtwtk_man/items/9d3db89dc4efe4697b5dを参考にしよう。

ちょっとついでに、zshのカラーもいじっておく...。

autoload -U colors
colors
PROMPT="${fg[cyan]}%n@%m:%d%# ${reset_color}"

# grep -n -e "Py.*Def" **/*.*

する。struct PyModuleDefが見えてないんだよねぇ...。

Oops... これ、ひょっとしてpython v3用か?
Migrating C extensions — Supporting Python 3: An in-depth guideに素敵なことが書いてある気がするのだが。

$ python --version
Python 2.7.6

うーん。

Pythonを高速化しよう! - gumi Engineer’s Blogとかc/c++をラップしてpythonで使えるように - Python | Welcome to undergroundがv2用だな。
文字列の扱いはhttp://qiita.com/mtwtk_man/items/9d3db89dc4efe4697b5d、数値の扱いはPythonからCプログラムを呼び出す | 象歩とかを参考にして...。
メモリ管理とか、本当はC/APIで文字列操作をして色々ハマったこと - YAMAGUCHI::weblogとか参考にしてPyMem_Mallocとか使うのかもしれないけど、面倒なので一旦見なかったことに(汗)
戻り値の生成はhttp://docs.python.jp/2.5/ext/buildValue.htmlでも参考に。pythonのバージョンが古そうだが、流石にこんなとこは共通だと信じたいので気にしない方向で。
文字列の連結は面倒くさいのでSTLを使うことにして...。

[Makefile]

TARGET=samplemodule01.so

CXXFLAGS += -I/usr/include/python2.7
LFLAGS   += -lpython2.7

OBJS = samplemodule01.o

################################################################################

.PHONY: all
all: $(TARGET)

$(TARGET): $(OBJS)
    $(CXX) -Wall -shared -o $(TARGET) $(OBJS) $(LFLAGS)

.cpp.o:
    $(CXX) -Wall -fpic $(CXXFLAGS) -c $<

.PHONY: clean
clean:
    rm -rf $(TARGET) $(OBJS)

#include <Python.h>
#include <string>

static
PyObject* add_triple(PyObject *self, PyObject *args)
{
    int i1, i2, i3;
    const char *s1, *s2, *s3;

    if ( PyArg_ParseTuple(args, "iii", &i1, &i2, &i3) ) {
        return Py_BuildValue("i", i1 + i2 + i3);
    }
    else if ( PyArg_ParseTuple(args, "sss", &s1, &s2, &s3) ) {
        std::string st1(s1), st2(s2), st3(s3);
        return Py_BuildValue("s", (st1 + st2 + st3).c_str());
    }
    return NULL;
}

static
PyMethodDef samplemodule01_methods[] = {
    {"add_triple", add_triple, METH_VARARGS, "add triple objects"},
    {NULL, NULL, 0, NULL}
};

#if PY_MAJOR_VERSION >= 3
static
struct PyModuleDef samplemodule01_module = {
    PyModuleDef_HEAD_INIT,
    "samplemodule01",
    "sample extension module",
    -1,
    samplemodule01_methods
};
#endif    /* PY_MAJOR_VERSION >= 3 */

PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_samplemodule01(void)
{
    return PyModule_Create(&samplemodule01_module);
}
#else    /* ! PY_MAJOR_VERSION >= 3 */
initsamplemodule01(void)
{
    Py_InitModule3("samplemodule01", samplemodule01_methods, "sample extension module");
}
#endif    /* ! PY_MAJOR_VERSION >= 3*/

とかをビルドしてみる。v3用には確認していない!
モジュールの仕様はsamplemodule01.add_triple()に3つの数値あるいは3つの文字列を与えると型の意味に応じた加算を行って返すっちゅー意味の分からないもんだが、ま、サンプルなんで。
ビルドしたらsamplemodule01.soができた。

テストするために、pythonスクリプトを書く。
[test.py]

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import samplemodule01

if __name__ == "__main__" :
    v = samplemodule01.add_triple(2, 3, 5)
    print("2+3+5=%d" % (v))

    v = samplemodule01.add_triple("YOU", "は", "SHOCK")
    print("%s 愛で空が 落ちてくる" % (v))

さて、試験運転。

$ ./tesy.py 
2+3+5=10
YOUはSHOCK 愛で空が 落ちてくる

ふむ。"You are shocked"でないらしいのでこれでいいのだ。shockには名詞形もあるらしいので「お前はショックだ」ってことなんだろうか。Google翻訳で「youはshock」と入れたら「あなたはショックをは」と返ってきたをは。