C や C++ による Python の拡張　（引数が複数ある場合、Python側からリストを渡す場合）

ちょっとだけ自分で作ってみたのでメモ

遺伝的アルゴリズムを走らせるときに利用した。気持ち早くなった。。。
作ったものとしては簡単な10進数ー2進数の変換とグレイコード変換もおまけで。
bitstring使えばすぐだけど練習の意味もこめて。

まずはじめにCのコード（一部）。インデントがなんか崩れてるけど、同じ処理何回も書いてるけど、命名規則もでたらめだけど、たたき台として。ちょっと長いですがそのままです。

ちなみにPython側からListを渡してC側で受け取るところでつまりました。
プログラム難しい...

参考(主にAPI関係)
http://stackoverflow.com/questions/5079570/writing-a-python-c-extension-how-to-correctly-load-a-pylistobject

http://rokujyouhitoma.hatenablog.com/entry/20100704/1278216557

http://www.hexacosa.net/documents/python-extending/

参考(ビット演算など)
http://saeki-ce.xsrv.jp/index.html

その他wikiなどなど


```cbinary.c
#include <Python.h>

#define LOWER_BOUND    0
#define UPPER_BOUND    1000
#define MAX_BIT_LENGTH 10

int binaryToValue(int *);

static PyObject *
valueToGray(PyObject *self, PyObject *args)
{
  //return gray code
  int i, n, m, len;
  unsigned int num;
  int b[MAX_BIT_LENGTH];
  PyListObject *binary;
  binary = (PyListObject *) PyList_New(MAX_BIT_LENGTH);

  if (!PyArg_ParseTuple(args, "i", &num)){
    return NULL;
  }
  n = (num >> 1) ^ num; //gray codeに変換

  //2進数に変換
  for (i=0; n>0; i++){
    m=n%2;   //2で割ったあまり
    n=n/2;  //2で割る
    b[i] = m;
  }

  len = i; //整数の2進数変換した時の長さ

  //10bitになるようにけつに0を追加する
  for (i=len; i<MAX_BIT_LENGTH; i++ ){
    b[i] = 0;
  }
 
  //逆順だったビット列を2進数の順にコピーする
  n = 0;
  for (i=MAX_BIT_LENGTH-1; i>=0; i--){
  //gray に1bitずつint型の値を入れていく
    PyList_SET_ITEM(binary, n++, Py_BuildValue("i", b[i]));
  }

  return Py_BuildValue("O", binary);
}


static PyObject *
make_individual(PyObject *self, PyObject *args)
{

  int b[MAX_BIT_LENGTH];
  int m,n,i,len;
  unsigned int min, max;
  unsigned int random_int;
  unsigned int gray_int;

  PyListObject *gray; //pythonのlistオブジェクトを表現
  gray = (PyListObject *) PyList_New(MAX_BIT_LENGTH); //サイズがMAX~のリストを作る

  if (!PyArg_ParseTuple(args, "ii", &min, &max)){ //引数が複数の時はiiみたいに並べて書く
    return NULL;
  }

  if (min < LOWER_BOUND){
    exit(0);
  }
  else if (max > UPPER_BOUND){
    exit(0);
  }
  //ランダムに整数を発生させる
  random_int = min + (int)(rand()*(max-min+1.0)/(1.0+RAND_MAX));
  gray_int = (random_int >> 1) ^ random_int; //gray codeに変換

  n = gray_int;
  //2進数に変換
  for (i=0; n>0; i++){
    m=n%2;   //2で割ったあまり
    n=n/2;  //2で割る
    b[i] = m;
  }

  len = i; //整数の2進数変換した時の長さ

  //10bitになるようにけつに0を追加する
  for (i=len; i<MAX_BIT_LENGTH; i++ ){
    b[i] = 0;
  }
 
  //逆順だったビット列を2進数の順にコピーする
  n = 0;
  for (i=MAX_BIT_LENGTH-1; i>=0; i--){
  //gray に1bitずつint型の値を入れていく
    PyList_SET_ITEM(gray, n++, Py_BuildValue("i", b[i]));
  }

  return Py_BuildValue("O", gray);
}

static PyObject *
grayToBinary(PyObject *self, PyObject *args)
{

  unsigned int num;
  unsigned int mask;
  int m,n,i,len;
  int b[MAX_BIT_LENGTH], inputed_binary[MAX_BIT_LENGTH];
  PyListObject *binary; //pythonのlistオブジェクトを表現
  PyObject *get_list;
  binary = (PyListObject *) PyList_New(MAX_BIT_LENGTH); 

  if (!PyArg_ParseTuple(args, "O", &get_list )){
    return NULL;
  }
  if PyList_Check(get_list) {
      for (i=0; i<PyList_Size(get_list); i++){
	//リストオブジェクトの中身をCで見れるように変換しながら取り出す?(自信なし)
	inputed_binary[i] = PyInt_AsSsize_t(PyList_GetItem(get_list, (Py_ssize_t)i)); //ok
      }
    }
  
  num = binaryToValue(inputed_binary);
  
  for (mask = num >> 1; mask != 0; mask = mask >> 1){
    //gray codeから元に戻す
    num = num ^ mask;
  }
  
  n = num;
  //2進数に変換
  for (i=0; n>0; i++){
    m=n%2;   //2で割ったあまり
    n=n/2;  //2で割る
    b[i] = m;
  }

  len = i; //整数の2進数変換した時の長さ

  //10bitになるようにけつに0を追加する
  for (i=len; i<MAX_BIT_LENGTH; i++ ){
    b[i] = 0;
  }
  
  //逆順だったビット列を2進数の順にコピーする
  n = 0;
  for (i=MAX_BIT_LENGTH-1; i>=0; i--){
    //binary に1bitずつint型の値を入れていく
    PyList_SET_ITEM(binary, n++, Py_BuildValue("i", b[i]));
  }

  return Py_BuildValue("O", binary);
}


int binaryToValue(int *b){
  //2進数を整数に変換する
  int i,n;
  i=0; n=0;

  while(i < MAX_BIT_LENGTH){
    if (b[i] == 1) n+=1;
    i+=1;
    if (i == MAX_BIT_LENGTH) break;
    n=n*2;
    //printf("%d\n", n);
  }
  return n;
}

static PyObject *
binaryToPtype(PyObject *self, PyObject *args)
{

  int i,n;
  int inputed_binary[MAX_BIT_LENGTH];
  PyListObject *binary; //pythonのlistオブジェクトを表現
  PyObject *get_list;

  if (!PyArg_ParseTuple(args, "O", &get_list )){ //pythonオブジェクトをそのまま渡す
    return NULL;
  }
  
  if PyList_Check(get_list) {
      for (i=0; i<PyList_Size(get_list); i++){
	inputed_binary[i] = PyInt_AsSsize_t(PyList_GetItem(get_list, (Py_ssize_t)i)); //ok
      }
    }
  
  i=0; n=0;

  while(i < MAX_BIT_LENGTH){
    if (inputed_binary[i] == 1) n+=1;
    i+=1;
    if (i == MAX_BIT_LENGTH) break;
    n=n*2;
    //printf("%d\n", n);
  }
  return Py_BuildValue("i", n);
}


static PyObject *
hello(PyObject *self)
{
    printf("Hello World!!\n");
    Py_RETURN_NONE;
}

static char ext_doc[] = "C extention module example\n";

static PyMethodDef methods[] = {
  {"hello", (PyCFunction)hello, METH_NOARGS, "print hello world.\n"},
  {"value_to_gray", valueToGray, METH_VARARGS, "return gray obj.\n"},
  {"make_individual", make_individual, METH_VARARGS, "return gray code.\n"},
  {"gray_to_binary", grayToBinary, METH_VARARGS, "return binary code.\n"},
  {"binary_to_ptype", binaryToPtype, METH_VARARGS, "return ptype value.\n"},
  {NULL, NULL, 0, NULL}
};

void initcbinarymethods(void)
{
    Py_InitModule3("cbinarymethods", methods, ext_doc);
}
```

いつものように? setup.pyを書きます。

```py:setup.py
from distutils.core import setup, Extension

module1 = Extension('cbinarymethods',
                    sources = ['cbinary.c'])

setup (name = 'cbinarymethods',
       version = '1.0',
       description = 'This is a demo package',
       ext_modules = [module1])
```

これまたいつものようにコマンドラインでビルドします。

```
$ python setup.py build_ext --inplace　#--inplaceをつけるとカレントに.soができるっぽい
```

無事ビルドできたらcbinarymethods.soができています。

pythonでimportして使ってみよう

```
>>> import cbinarymethods as cbm
>>> i = cbm.make_individual(200, 250)
>>> i
[0, 0, 1, 0, 0, 0, 1, 0, 1, 1]
>>> a = cbm.gray_to_binary([0,0,1,0,1,0,0,1,0,1])
>>> a
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0]
```