bytesからchar*への変換

python3のマルチバイト文字列であるところのbytearraybytesをcのchar buf[20]に代入したい。

textbuffer.h

struct TextBuffer
{
  TextBuffer();
  char buf[20];
};

textbuffer.cpp

TextBuffer::TextBuffer()
{
}

textbuffer.i

%module textbuffer
%{
#include "textbuffer.h"
}
%include "textbuffer.h"

setup.py

# coding: utf-8

from distutils.core import setup, Extension
import os
import sys

setup(
  name='textbuffer',
  version='0.1',
  description='textbuffer module',
  ext_modules=[
      Extension(
                '_textbuffer',
                [
                 'textbuffer.i',
                 'textbuffer.cpp',
                ],
                swig_opts=['-c++'],
               ),
       ],
  py_modules=['textbuffer'],
)
# python3 setup.py build

build/lib.win32-3.1/textbuffer.pyを使う

>>> a=textbuffer.TextBuffer()
>>> a.buf="abc"
>>> print(a.buf)
abc

# bytearrayの代入でエラー
>>> a.buf="abc".encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "textbuffer.py", line 71, in <lambda>
    __setattr__ = lambda self, name, value: _swig_setattr(self, TextBuffer, name
, value)
  File "textbuffer.py", line 48, in _swig_setattr
    return _swig_setattr_nondynamic(self,class_type,name,value,0)
  File "textbuffer.py", line 41, in _swig_setattr_nondynamic
    if method: return method(self,value)
TypeError: in method 'TextBuffer_buf_set', argument 2 of type 'char [20]'
>>>

解決

細かいことは良くわからないがとりあえず動く方法。
const char *を引数に取るセッターを追加して、それに対するbytearrayからのtypemapを設定することでできるようだ。
textbuffer.iに追加

// bytearrayからconst char *へのtypemap
%include <pybuffer.i>
%pybuffer_string(const char *src);

// setを追加
%{
#include <string.h>
%}
%extend TextBuffer {
   void set(const char *src) {
     strncpy(self->buf, src, 20);
   }
};

動作確認

>>> import textbuffer
>>> a=textbuffer.TextBuffer()
>>> a.set("abc".encode("ascii"))
>>> print(a.buf)
abc
# CP932でやってみる(CP932->PyUnicodeで失敗してる)
>>> a.set("日本語".encode("cp932"))
>>> print(a.buf)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf8' codec cant decode byte 0x93 in position 0: unexpected code byte
# UTF-8でやってみる(問題なし)
>>> a.set("日本語".encode("utf-8"))
>>> print(a.buf)
日本語
>>>

python3+swigでは素では
str <-> const char *(utf-8)
str <-> const wchar_t *(unicode)
が基本。
それ以外だと
str.encode('cp932') -> const char*(pybuffer_string等で変換指定)
というようながんばりが必要みたいだ。
文字コードとの戦いは果てしない。

pybuffer_string
についてはこの辺参考。
http://www.swig.org/Doc1.3/Python.html#Python_nn75
そろそろswigのtypemapをちゃんと理解したいところだな。