読者です 読者をやめる 読者になる 読者になる

pybindgenでC++クラスを呼び出す

いろいろカンでやったとこもあるので間違っているところがあればツッコミお願いします。

前提とする環境

  • Windows XP 32bit
  • cygwin
  • VisualC++ 2008 Express Edition (gccxmlは古いので2010に対応していない)
  • Python2.7

gccxmlのインストール

http://sourceforge.net/projects/pygccxml/files/gccxml-setup/gccxml-7-JAN-2009-rev1.127/
gccxml-0.9.0-win32-x86-rev1.127.exe をダウンロードしてインストール

$ cd "%PROGRAMFILES%\gccxml 0.9\bin"
$ ./gccxml_vcconfig.bat

これでVisualC++のヘッダをgccxml以下へコピーされる これインストーラで実行されているっぽい --2012.03.09 追記

環境変数に以下を追加

GCCXML_COMPILER=msvc9      # もしくはVC++2005ならmsvc8
GCCXML_FLAGS=-I.
GCCXML_DIR=%PROGRAMFILES%\gccxml 0.9\bin
PATH=%PATH%;%GCCXML_DIR%   # gccxml.exeを呼び出せるようにパスを通しておく

pygccxmlのインストール

http://sourceforge.net/projects/pygccxml/files/pygccxml/pygccxml-1.0/
pygccxml-1.0.0.zipをダウンロード

$ pip install pygccxml-1.0.0.zip

$ pydoc pygccxml
Help on package pygccxml:

NAME
    pygccxml - Python GCC-XML front end.

FILE
    c:\python27\lib\site-packages\pygccxml\__init__.py

DESCRIPTION
    This package provides functionality to extract and inspect
    declarations from C/C++ header files. This is accomplished
    by invoking the external tool U{gccxml<http://www.gccxml.org/>}
    which parses a header file and dumps the declarations as a

... 中略 ....

VERSION
    1.0.0

pybindgenのインストール

http://code.google.com/p/pybindgen/downloads/detail?name=pybindgen-0.15.0.zip
pybindgen-0.15.0.zipをダウンロード

$ unzip pybindgen-0.15.0.zip
$ cd pybindgen-0.15.0
$ PYTHON=C:/Python27/python.exe ./waf configure --prefix C:/Python27
$ ./waf install

$ cd /                                    # pybindgenフォルダの中にいるとそっちをimportしてしまうため
$ pydoc pybindgen
Help on package pybindgen:

NAME
    pybindgen

FILE
    c:\python27\lib\site-packages\pybindgen\__init__.py

PACKAGE CONTENTS
    container
    converter_functions
    cppattribute
    cppclass

... 以下略 ....

$ pydoc pybindgen.version
Help on module pybindgen.version in pybindgen:

NAME
    pybindgen.version

FILE
    c:\python27\lib\site-packages\pybindgen\version.py

DATA
    __version__ = [0, 15, 0]

VERSION
    [0, 15, 0]
補足 --2012.02.23

x64環境でVisualStudio2008/2010が両方インストールされた環境下だと waf configure がエラーになるみたい。
pybindgen-0.15.0/waf-1.5.9-(略)/wafadmin/Tools/msvc.py
↑ここでMSVC_TARGETSをちゃんと渡せればx86指定できそうなんだけども、
やり方が見つけられなかったので

- all_msvc_platforms=[('x64','amd64'),('x86','x86'),('ia64','ia64'),('x86_amd64','amd64'),('x86_ia64','ia64')]
+ all_msvc_platforms=[('x86','x86')]

ぶしっとx86オンリーに修正してリトライ。

C++コードの作成

// hum.h
#pragma once

class Human
{
public:
    Human();
    virtual ~Human();

    void sayHello(const char* message);
};
// hum.cpp
#include "hum.h"
#include <iostream>

using namespace std;

Human::Human()
{
    cout << "Human.ctor" << endl;
}
Human::~Human()
{
    cout << "Human.dtor" << endl;
}

void Human::sayHello(const char* message)
{
    cout << "Human: hello, " << message << "!!" << endl;
}

pybindgenでPythonモジュールを生成

// modgen.py
import sys
import pybindgen
from pybindgen.gccxmlparser import ModuleParser

name = sys.argv[1]
headers = sys.argv[2:]

parser = ModuleParser(name, '::')
module = parser.parse(headers)
for s in headers:
    module.add_include('"%s"' % s)

module.generate(sys.stdout)
$ python modgen.py hum hum.h > hum_mod.cpp
INFO Parsing source file "hum.h" ... 
INFO gccxml cmd: ""C:\Program Files\gccxml 0.9\bin\gccxml.exe"  -I"."   "hum.h" -fxml="c:\temp\tmpgm8k3y.xml""
INFO GCCXML version - 0.9

Pythonモジュールのコンパイル

// setup.py
from distutils.core import setup, Extension

setup(
    ext_modules = [
        Extension(
            'hum',
            ['hum.cpp', 'hum_mod.cpp'],
            extra_compile_args=['/EHsc']),
        ])
$ python setup.py build
running build
running build_ext
building 'hum' extension
creating build
creating build\temp.win32-2.7
creating build\temp.win32-2.7\Release
C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -IC:\Python27\include -IC:\Python27\PC /Tphum.cpp /Fobuild\temp.win32-2.7\Release\hum.obj /EHsc
hum.cpp
C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -IC:\Python27\include -IC:\Python27\PC /Tphum_mod.cpp /Fobuild\temp.win32-2.7\Release\hum_mod.obj /EHsc
hum_mod.cpp
creating build\lib.win32-2.7
C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\link.exe /DLL /nologo /INCREMENTAL:NO /LIBPATH:C:\Python27\libs /LIBPATH:C:\Python27\PCbuild /EXPORT:inithum build\temp.win32-2.7\Release\hum.obj build\temp.win32-2.7\Release\hum_mod.obj /OUT:build\lib.win32-2.7\hum.pyd /IMPLIB:build\temp.win32-2.7\Release\hum.lib /MANIFESTFILE:build\temp.win32-2.7\Release\hum.pyd.manifest
   ライブラリ build\temp.win32-2.7\Release\hum.lib とオブジェクト build\temp.win32-2.7\Release\hum.exp を作成中
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\mt.exe -nologo -manifest build\temp.win32-2.7\Release\hum.pyd.manifest -outputresource:build\lib.win32-2.7\hum.pyd;2

$ cp -f build/lib.win32-2.7/hum.pyd hum.pyd
$ pydoc hum
Help on module hum:

NAME
    hum

FILE
    c:\work\test\hum.pyd

SUBMODULES
    std

CLASSES
    __builtin__.object
        Human
    
    class Human(__builtin__.object)
     |  Methods defined here:
     |  
     |  __copy__(...)
     |  
     |  __eq__(...)
     |      x.__eq__(y) <==> x==y
     |  
     |  __ge__(...)
     |      x.__ge__(y) <==> x>=y
     |  
     |  __gt__(...)
     |      x.__gt__(y) <==> x>y
     |  
     |  __init__(...)
     |      x.__init__(...) initializes x; see help(type(x)) for signature
     |  
     |  __le__(...)
     |      x.__le__(y) <==> x<=y
     |  
     |  __lt__(...)
     |      x.__lt__(y) <==> x<y
     |  
     |  __ne__(...)
     |      x.__ne__(y) <==> x!=y
     |  
     |  sayHello(...)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |  
     |  __new__ = <built-in method __new__ of type object>
     |      T.__new__(S, ...) -> a new object with type S, a subtype of T

Humanが使えるようになっているっぽいですね。
あれ?stdって何だろう?

テスト

$ python
>>> from hum import Human
>>> human = Human()
Human.ctor
>>> human.sayHello('hoge')
Human: hello, hoge!!
>>> del human
Human.dtor

動いた!!

gccxmlを使って完全に自動化しているので、もしかしたら何か対応できていないことなどクセがあるのかも。
これは楽ちんだ。しばらく使ってみようと思います。

オマケ
// Makefile
.PHONY: test clean

test: hum.pyd
	python test.py

hum.pyd: build/lib.win32-2.7/hum.pyd
	cp -f $< $@

build/lib.win32-2.7/hum.pyd:  hum_mod.cpp *.cpp *.h
	python setup.py build

hum_mod.cpp: *.h
	python modgen.py hum $^ > $@

clean:
	$(RM) -r build/ *.pyd *_mod.cpp
$ make