C/C++側のポインタを Python で持っておく方法。
なにか、C++のクラスをPythonから使いたいとする。
class Hello {
public:
void hello();
};
PyCapsule_New
C/C++のポインタを PyObject*
として返すには PyCapsule_New
を使う。
static void py_hello_free(PyObject *obj);
static PyObject* py_hello_alloc(PyObject *self, PyObject *args)
{
// Allocate a new C++ pointer h as a Python object "_Hello"
Hello* const h= new Hello();
return PyCapsule_New(h, "_Hello", py_hello_free);
}
ポインタを解放する関数を渡しておくと、Python側で解放してくれる。メモリはC/C++側で管理して解放の必要がない場合は py_hello_free のかわりに NULL を渡しておけばよい。
void py_hello_free(PyObject *obj)
{
// Destructor function for _Hello pointer, called automatically from Python
Hello* const h= (Hello*) PyCapsule_GetPointer(obj, "_Hello");
delete h;
}
PyCapsule_GetPointer
C/C++ のポインタに戻すには PyCapsule_GetPointer
:
PyObject* py_hello(PyObject *self, PyObject *args)
{
// Use _Hello pointer from Python
PyObject* py_obj;
if(!PyArg_ParseTuple(args, "O", &py_obj))
return NULL;
Hello* h= (Hello*) PyCapsule_GetPointer(py_obj, "_Hello");
if(h == 0) return NULL;
h->hello();
Py_RETURN_NONE;
}
C/C++ の拡張を作るいつものコード
#include <cstdio>
#include "Python.h"
// ... 上のコード ...
static PyMethodDef methods[] = {
{"hello_alloc", py_hello_alloc, METH_VARARGS, "allocate a Hello object"},
{"hello", py_hello, METH_VARARGS, "call hello() method"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"cext02", // name of this module
"Use C/C++ pointer", // Doc String
-1,
methods
};
PyMODINIT_FUNC
PyInit_cext02(void) {
return PyModule_Create(&module);
}
いつもの setup.py
from distutils.core import setup, Extension
setup(name='cext02',
version='0.0.1',
description='c_ext02 pointer',
author='Jun Koda',
url='https://github.com/junkoda/python_c_ext',
ext_modules=[
Extension('cext02', ['cext02.cpp'])
]
)
コンパイル
$ python3 setup.py build_ext --inplace
Python から
import cext02 # Cによる拡張
h = cext02.hello_alloc() # h = new Hello() みたいなこと
cext02.hello(h) # h->hello() を呼ぶ
のように使う。(もちろん、Python の class でラッピングすれば、オブジェクトっぽく使える。)
参考
-
Cの拡張をはじめるにあたって、まずは Python Cookbook を読むといいと思う。
Python Cookbook 3rd Edition (O'Reilly) -
Code: https://github.com/junkoda/python_c_ext/tree/master/02_pointer