View C / C ++ data as np.array from Python.
struct Particle {
float x[3], v[3];
};
vector<Particle> v;
And suppose data like Particle *
is on the C / C ++ side.
numpy.array from C/C++
First, include numpy / arrayobject.h
and initialize it. This seems to initialize static variables, and if you have more than one source code, you have to call ʻimport_array ()` in all the source code that uses numpy. Otherwise, you will get a segmentation fault.
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "numpy/arrayobject.h"
PyMODINIT_FUNC
PyInit_cext07(void) {
import_array();
return PyModule_Create(&module);
}
Set ʻinclude_dirs in
setup.py` to read the NumPy headers.
import numpy as np
from distutils.core import setup, Extension
setup(name='cext07',
version='0.0.1',
description='c_ext07 read data as np.array',
author='Jun Koda',
url='https://github.com/junkoda/python_c_ext',
ext_modules=[
Extension('cext07', ['cext07.c'],
include_dirs = [np.get_include()]),
],
)
A function that passes a pointer to vector <Particle>
so that it can be referenced from the Python side:
static void py_particles_free(PyObject *obj);
static PyObject* py_particles_alloc(PyObject *self, PyObject *args)
{
// Allocate a new C++ pointer as a Python object "_Particles"
vector<Particle>* const p= new vector<Particle>();
p->resize(10); //Assuming that the data has 10 particles
return PyCapsule_New(p, "_Particles", py_particles_free);
}
void py_particles_free(PyObject *obj)
{
// Destructor function for _Particles pointer, called automatically from Python
vector<Particle>* const p=
(vector<Particle>*) PyCapsule_GetPointer(obj, "_Particles");
delete p;
}
Functions that pass vector <Particle>
as np.array
:
PyObject* py_as_array(PyObject *self, PyObject *args)
{
PyObject* py_particles;
if(!PyArg_ParseTuple(args, "O", &py_particles))
return NULL;
// Get vector<Particle>* pointer back from _Particles
vector<Particle>* const p=
(vector<Particle>*) PyCapsule_GetPointer(py_particles, "_Particles");
if(!p) return NULL;
int nd=2; //Two-dimensional array with rows and columns
int ncol= sizeof(Particle)/sizeof(float); // ncol=6 x[3], v[3]6 rows
npy_intp dims[]= {(npy_intp) p->size(), ncol};
return PyArray_SimpleNewFromData(nd, dims, NPY_FLOAT, &(p->front()));
}
Usual C / C ++ extension functions
static PyMethodDef methods[] = {
{"_particles_alloc", py_particles_alloc, METH_VARARGS, "new Particles"},
{"_as_array", py_as_array, METH_VARARGS, "vector<Particle> as an array"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"cext07", // name of this module
"Use vector<Particle>", // Doc String
-1,
methods
};
import numpy as np
import cext07
class Particles:
def __init__(self):
self._p= cext07._particles_alloc() # _Particle pointer
def __repr__(self):
return cext07._as_array(self._p).__repr__()
def __getitem__(self, index):
return cext07._as_array(self._p)[index]
def asarray(self):
return cext07._as_array(self._p)
#etc
import cext07
p = Particles()
p # __repr__Sees array by
p[0] # __getitem__;First particle
p[2:5,2] #slice is also np.array handles it
p[:,0] # x[0]
p[:,4] # v[1]
p[:] #All data
In this example, I didn't need a Python class, just the as_array function. I will simplify it later.
The full code can be found on GitHub.
Recommended Posts