#keywords(Python,API,cStringIO,PyTypeObject)
#floatcontents
* Python C API使用メモ [#d0126eab]
PythonからCを使用したときのメモです。Cはあまり得意ではないのでおかしい部分もあるかも・・・~
PythonからCを呼び出す基本的な方法は検索すればいくつか出てくると思いますので、ここでは取り上げません。PerlXSと比べてかなり楽だと思います。
:参考|
- 象歩:PythonからCプログラムを呼び出す -- http://owa.as.wakwak.ne.jp/zope/docs/Python/BindingC/
-- PythonからCのコードをコールする方法が丁寧に解説されています。感謝⌣
PythonからCのコードを呼び出すのは基本さえ理解すればそんなに難しくないので知っておいてもいいかも。
** cStringIOを使う [#h6972bdc]
#anno2(2011-07-05)
どのぐらい速度にメリットがあるかは分かりませんが(Python側からもC側からも同じバッファにアクセスしまくりの時は役に立つ?)、cStringIOのアクセスを行ってみます。~
cStringIOへのアクセスについては/usr/include/python/cStringIO.hに関数などは記述されていますが、ドキュメントは見つかりませんでしたので適当に使ってみました。~
:CでcStringIOオブジェクトへ書き込む場合|
#include <Python.h>
#include <cStringIO.h>
PyObject* cstr_write_from_c(PyObject *self, PyObject *args) {
PyObject *pycStr;
PycString_IMPORT;
if(!PyArg_ParseTuple(args, "O", &pycStr)) return NULL;
if(PycStringIO_OutputCheck(pycStr)){
PycStringIO->cwrite(pycStr, "Hello", 5);
}else{
printf("Arg1 is not cStringIO object.\n");
}
return Py_BuildValue("");
}
PycStringIOを使う時は''PycString_IMPORT''マクロで初期化する必要があることに注意してください。これをPythonからコール(引数はcStringIOオブジェクト)するとcStringIOに"Hello"と書き込まれます。その他のメソッドについてはcStringIO.hを参照してみてください。~
双方から文字列へのアクセスするのにこんな方法もある、ってことで。
** 組み込み型の比較 [#j85718e9]
#anno2(2011-07-05)
引数の型をチェックするのは簡単ですが、引数に組み込み型を渡されたときのチェックに少しつまずいたのでメモ。動作はしましたが、正しいかどうかはちょっと微妙&huh;
:引数が組み込み型のstrかどうかをチェックします|
#include <Python.h>
PyObject* cstr_chkstr(PyObject *self, PyObject *args) {
PyObject *pyType;
if(!PyArg_ParseTuple(args, "O", &pyType)) return NULL;
if(PyType_IsSubtype((PyTypeObject*)pyType, &PyString_Type)){
printf("Arg1 is str type.\n");
}
return Py_BuildValue("");
}
組み込み型はPyTypeObjectで表現され、型の比較は''PyType_IsSubtype(PyTypeObject*, PyTypeObject*)''で行われます。ここで使用している''PyString_Type''はPythonのstr型を表す実体です。
** PythonファイルハンドラとFILE構造体(Windows) [#rb9de1c4]
#anno2(2011-07-15)
FAQ級の問題みたいですが、ちゃんとした解説が見つからなかったのでメモしておきます。MinGW使いの人には常識なのかも。Linux環境下では発生しません。~
Windows環境において、Python側で生成したファイルハンドラをPyFile_AsFile()でC側のファイルポインタに変換したときにセグメンテーションフォルトを起こすという問題があるようです。以下に例を示します。
:mymod.c|
#include <stdio.h>
#include <Python.h>
PyObject* mymod_hello(PyObject *self, PyObject *args) {
PyObject *pyFh;
FILE *fp;
if(!PyArg_ParseTuple(args, "O", &pyFh)) return NULL;
fp = PyFile_AsFile(pyFh);
fprintf(fp, "Hello, world!\n");
return Py_BuildValue("");
}
static PyMethodDef objmethods[] = {
{"hello", mymod_hello, METH_VARARGS},
{NULL},
};
void initmymod() {
Py_InitModule("mymod", objmethods);
}
:call.py|
import mymod
fh = open("test.out", "wb")
mymod.hello(fh)
モジュールメソッドmymod.hello()をcall.pyから呼び出すという単純な例です。Pythonで書き出し用ファイルをopenし、mymod_hello()でファイルポインタに変換してHello, world!!を書き込むという手順です。~
このmymod.cをMinGWのgccでビルドして、call.pyをWindows版Pythonで実行すると、セグメンテーションフォルトで落ちてしまいます。なお、cygwin環境で行った場合(実行もcygwinのPythonを使う)は問題ありません。~
これは使用するPythonにリンクされているMSVCRT(ランタイム)ライブラリのバージョンと作成したmymodのそれが一致していないことが問題で、どうやらファイルポインタのバイナリ互換性がないことが原因らしいです。
- How to receive a FILE* from Python under MinGW -- http://code.activestate.com/lists/python-list/563094/
通常MinGWでビルドした際はmsvcrt.dllがリンクされますが、併せて使用するPythonに一致したMSVCRTをリンクする必要があります。MinGW gccの非公式リリースのhttp://www.develer.com/oss/GccWinBinariesによると、
>
- For Python 2.6, 2.7, 3.0 or 3.1, you want MSVCR90.DLL
- For Python 2.4 or 2.5, you want MSVCR71.DLL
- For Python 2.3 or older, you want MSVCRT.DLL
だそうです。従って、Python 2.7が対象の場合はMSVCR90.DLLをリンクすればいいことになります。以下に簡単なMakefileの例を示します(PythonはC:\Python27にインストールされていると仮定します)。
:Makefile|
CC=gcc
CFLAGS=-I/c/Python27/include
LDFLAGS=-L/c/Python27/libs
LIBS=-lpython27 &color(red){-lmsvcr90};
mymod.pyd: mymod.o
$(CC) -shared $(LDFLAGS) -o mymod.pyd mymod.o $(LIBS)
mymod.o: mymod.c
$(CC) $(CFLAGS) -o mymod.o -c mymod.c
ビルドで作成したmymod.pydがリンクしているDLLを見てみると、
$ objdump -p mymod.pyd | grep "DLL Name"
DLL Name: python27.dll
DLL Name: KERNEL32.dll
DLL Name: msvcr90.dll
DLL Name: msvcrt.dll
となれば成功です。このようにビルドしておくと想定通り動作してくれます⌣。例では示していませんがos.popenによるパイプ処理も同様の問題があるので同じように対応可能です。
*** 補足 [#tdf7d0e6]
- http://www.develer.com/oss/GccWinBinariesを使った場合specsファイルを書き換えて同様の効果が得られますが、上記の方がお手軽かも。
- MinGW GCCでlibcとしてmsvcrt.dll以外を使う -- http://d.hatena.ne.jp/y2q_actionman/20070614/p1
-- この記事ではmsvcrt.dllを一切リンクしないようにしていますが、私の環境では
ImportError: DLL load failed: 指定されたプロシージャが見つかりません。
というエラーが出たので上記の方法に落ち着きました。私自身あまりgccの動作に詳しくないので、回避できなかっただけかも知れませんが、msvcrt.dllがリンクされていても特に問題はなさそうなのでよしとします⌣
** コメント [#i6d0cd05]
#comment