Welcome to underground

index

c/c++ wrapper for python

c/c++の遺産をpythonでも使いたい!
そんなあなたに朗報!
今お使いのものがpythonでもe使えますよ!
しかも無料ですよ!

参考にすべきリンク先

  1. PythonからCプログラムを呼び出す | 象歩
  2. PythonからMinGWで作ったCの関数を呼んでみる | BTY備忘録
  3. SWIGでPythonラッパを書いてみる - 睡眠不足?!
  4. SWIG and Pythono
  5. Boost.Python - Boost 1.49.0
  6. boost.python - Pythoninfo Wiki
  7. Boost.Python の機能をざっと紹介してみる

1,2はPython.hを使った伝統的な方法. 3,4はSwigを使ってユーザーが楽する方法. 5はc++もライブラリBoostの中のPythonを使ってかなり効率的に作る方法. 6もboost.pythonの話だがpython公式ドキュメント (クラス定義の項目が若干違う気がする.この通りに作ると上手くいかない. 上手く行ったやり方は下記を参照のこと). 7はlistまで突っ込んで話をしている.

どれも一長一短でどれが簡単かは一概には言えない. 個人的には,Boost.Python使うほうが良いのではないかと思います. Python.hはなんとなく古臭いし,ユーザーが書くべきコードが多くてC++の拡張が面倒くさい(かも知れない). Python.hをもう少し使いやすくしたのがBoost.Pythonというわけだ.

ここではPython.hとBoost.Pythonについてだけ記す. Swigはリンク3が詳しい. Swigはもちろんpythonだけでなく何でもラップしてくれるステキなヤツなので,興味があれば使ってみるとよろしいかと.

どれがえーねん?

結局の所,文法さえ覚えてしまえばどれを使っても良いのだ. とにかくpythonで動けばよいので,途中の形はあまり関係ない.
であるからして,好きなものをお使いください. 個人的には,簡単にCのクラスをpythonに持って来られるboost.pythonが好き.

実戦

python.h

習うより慣れろ,という事でまずは伝統的な方法から.

hello.c

// hello.c
#include <stdio.h>

int add(int x, int y){
  return x + y;
}

void out(const char* adrs, const char* name){
  printf("hello, my name is %s %s.\n", adrs, name);
}

それのラッパー,helloWrap.c

// helloWrap.c
#include "Python.h"


extern int add(int, int);
extern void out(const char*, const char*);


PyObject* hello_add(PyObject* self, PyObject* args){
  int x, y, g;

  if(!PyArg_ParseTuple(args, "ii", &x, &y)){
    return NULL;
  }else{
    g = add(x, y);
    return Py_BuildValue("i", g);
  }
}

PyObject* hello_out(PyObject* self, PyObject* args, PyObject* kw){

  const char* adrs = NULL;
  const char* name = NULL;
  static char* argnames[] = {"adrs", "name", NULL};

  if(!PyArg_ParseTupleAndKeywords(args, kw, "|ss", argnames, &adrs, &name)){
    return NULL;
  }else{
    out(adrs, name);
    return Py_BuildValue("");
  }
}


static PyMethodDef hellomethods[] = {
  {"add", hello_add, METH_VARARGS},
  {"out", hello_out, METH_VARARGS | METH_KEYWORDS},
  {NULL},
};


void initchello(){
  Py_InitModule("hello", hellomethods);
}

できたやつをコンパイル

 
gcc -fPIC -Wall -c -o hello.o hello.c
gcc -fPIC -Wall -c -o helloWrap.o helloWrap.c
gcc -fPIC -Wall -shared -o hellomodule.so hello.o helloWrap.o

つかってみよう

>>> import hello
>>> hello.add( 1, 3 )
4
>>> hello.out( 'john', 'do')
hello, my name is john do.

できたね. バッチリだね.

boost.python

でも面倒くさくないですか?確かにpythonの仕様にあわせて型を決めて使うっていうのは. しかもそれを自分で全部する必要があるという…. もちろんc++でも書けるし,コンパイルしてちゃんとpythonから読める.
だけれでもやはり,面倒なことには変わりない.と言う事でこの作業思い切って省いたのがboost.pythonというわけだ.

simple.cxx

include <boost/python.hpp>


char const* hello(){
  return "hello, world";
}


BOOST_PYTHON_MODULE( simple ){
  
  boost::python::def( "hello", hello );

}

コンパイル

g++ -fPIC -Wall -O2 -I/usr/include/python2.6 -lboost_python -shared -o simple.so simple.cxx

pythonから呼んでみよう

>>> import simple
>>> simple.hello()
hello, world

ちゃんと呼べたね. で,ユーザーが書くコードはたったの3行. さきのPyObjectを使う方法とは本当に違う.

boost.python 続き

もうちょっと複雑な場合を書く. ちょと長くなるけどステップバイステップでゆっくりと行きましょう.

wrapperを作るためのコードとオリジナルのc++コードを分離して書く.

  • simple.hxx
  • simple.cxx
  • simplew.cxx

simple.hxx

// simple.hxx
void hello( void );
const char* place ( void );
std::string year( void );
long add( long, long );

simple.cxx

#include <string>
#include <iostream>
#include "simple.hxx"

void hello( void ){
  std::cout << "Hello, world" << std::endl;
}

const char* place( void ){
  std::string s1( "ここはどこ?" );
  return s1.c_str();
}

std::string year( void ){
  std::string s2( "1900-4-8" );
  return s2;
}

long add( long a, long b ){
  return a + b;
}

simplew.cxx

#include <boost/python.hpp>
#include "simple.hxx"

BOOST_PYTHON_MODULE( simple2 ){
  
  boost::python::def( "hello", &hello );
  boost::python::def( "place", &place );
  boost::python::def( "year", &year );
  boost::python::def( "add", &add );

}

でコンパイル. g++ -fPIC -shared -lboost_python -I/usr/include/python2.6 -o simple2.so simple.cxxx simplew.cxx
そしたら呼び出し.

>>> import simple2
>>> simple2.hello()
Hello, world
>>> simple2.place()
ここはどこ?
>>> simple2.year()
1900-4-8
>>> simple.add( 10, 2 )
12

呼び出せましたね.

クラス

ステップバイステップ.Cのクラスをpythonで使えるようにしましょう.

#include &lt;iostream&gt;


class Abc{

public:
  Abc( int i ){ Abc::setN( i ); };
  ~Abc(){};
  void xyz( void ){ std::cout &lt;&lt; "hello" &lt;&lt; std::endl; };
  void setN( int i ){ m = i; };
  int getN( void ){ return m; };

private:
  int m;

};

BOOST_PYTHON_MODULE( call_class ){

  boost::python::class_&lt;Abc&gt;( "Abc", 
			      boost::python::init&lt; int &gt;() )
    .def( "xyz", &amp;Abc::xyz )
    .def( "setN", &amp;Abc::setN )
    .def( "getN", &amp;Abc::getN )
    ;

}

g++ -fPIC -shared -I/usr/include/python2.6 -lboost_python -o klass.so klass.cxx

>>> import klass
>>> a = klass.Abc()                                          
Traceback (most recent call last):                           
File "<stdin>", line 1, in <module>                        
Boost.Python.ArgumentError: Python argument types in         
    Abc.__init__(Abc)                                        
did not match C++ signature:                                 
    __init__(_object*, int) klass.Abc()
>>> a = klass.Abc( 10 )
>>> a.getN()
10
>>> a.setN( 20 )
>>> a.getN()
20
リスト

listが使えると良いのですが,c++で定義して呼べるのでしょうか?
もちろん呼べます.が,面倒ですので割愛.