Skip to content

Instantly share code, notes, and snippets.

@zed

zed/.gitignore Secret

Created September 26, 2011 13:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zed/84de2115f164d1ad031e to your computer and use it in GitHub Desktop.
Save zed/84de2115f164d1ad031e to your computer and use it in GitHub Desktop.
convert c array to numpy array in python callback provided by ctypes
/build/
/pointer2ndarray.cpp
/pointer2ndarray.h
/pointer2ndarray.so
/pointer2ndarray_api.h
/test_pointer2ndarray
/call_callback.o
/call_callback.so
#include <iostream>
#include "call_callback.h"
void call_callback(bool (*callback)(double*, long long)) {
double data[] = {3,2,1};
if (!callback(data, sizeof(data)/sizeof(data[0])))
std::cerr << "callback failed" << std::endl;
}
#ifndef HAVE_call_callback
#define HAVE_call_callback
#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C extern
#endif
EXTERN_C void call_callback(bool (*)(double *, long long));
#endif /* !HAVE_call_callback */
NAME = pointer2ndarray
all: test test_frompython test_call_callback
test: test_pointer2ndarray $(NAME).so
env PYTHONPATH=. ./$<
test_frompython: test_pointer2ndarray.py $(NAME).so
python $<
test_pointer2ndarray: test_pointer2ndarray.cpp pointer2ndarray_api.h $(NAME).so
$(CXX) -g -Wall -I/usr/include/python2.6 -lpython2.6 $< -o $@
test_call_callback: test_call_callback.py $(NAME).so call_callback.so
LD_LIBRARY_PATH=. python $<
call_callback.so: call_callback.o
$(CXX) -shared -Wl,-O1 -Wl,-Bsymbolic-functions $< -o $@
call_callback.o: call_callback.cpp call_callback.h
$(CXX) -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -fPIC -c $< -o $@
pointer2ndarray_api.h $(NAME).so: setup.py $(NAME).pyx
python setup.py build_ext -i
clean:
git clean -x -d -f
from libcpp cimport bool
cimport libc.string # memcpy
import numpy as _np
cimport numpy as np
np.import_array() # initialize C API to call PyArray_SimpleNewFromData
cdef public api bool ptsetDataSource(double* data, long long size) with gil:
if not data: return False
print "ptsetDataSource",
tonumpyarray(data, size)
return True
cdef public api bool ptsetDataSourceCopy(double* data, long long size) with gil:
if not data: return False
cdef np.ndarray[np.float_t,ndim=1] a = _np.empty(size, dtype=_np.float)
assert a.shape[0] == size and a.itemsize == sizeof(data[0])
assert a.flags['C_CONTIGUOUS']
libc.string.memcpy(a.data, <char*>data, a.nbytes)
print "ptsetDataSourceCopy", repr(a)
return True
cpdef call_ptsetDataSource(np.ndarray[np.float_t,ndim=1] data):
assert data is not None
if not data.flags['C_CONTIGUOUS']:
data = _np.ascontiguousarray(data)
print "call_ptsetDataSource",
if not ptsetDataSource(<double*>data.data, data.shape[0]):
raise RuntimeError("call_ptsetDataSource(%s)" % (data,))
cdef public api tonumpyarray(double* data, long long size) with gil:
assert data and size >= 0
cdef np.npy_intp dims = size
#NOTE: it doesn't take ownership of `data`. You must free `data` yourself
cdef np.ndarray[np.float_t,ndim=1] a = np.PyArray_SimpleNewFromData(
1, &dims, np.NPY_DOUBLE, <void*>data)
print "tonumpyarray", repr(a)
return a
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
setup(ext_modules=[Extension(
"pointer2ndarray", # name of extension
["pointer2ndarray.pyx"], # our Cython source
language="c++", # bool type
)],
cmdclass={'build_ext': build_ext})
#!/usr/bin/env python
from ctypes import (PYFUNCTYPE, py_object, POINTER, c_double, c_longlong,
pydll, CFUNCTYPE, c_bool, cdll)
import pointer2ndarray
tonumpyarray = PYFUNCTYPE(py_object, POINTER(c_double), c_longlong)(
("tonumpyarray", pydll.LoadLibrary(pointer2ndarray.__file__)))
@CFUNCTYPE(c_bool, POINTER(c_double), c_longlong)
def callback(data, size):
a = tonumpyarray(data, size)
# call scipy functions on the `a` array here
return True
cpplib = cdll.LoadLibrary("call_callback.so") # your C++ lib goes here
cpplib.call_callback(callback)
// Embed Python interpreter and call C API function from
// pointer2ndarray Python module
#include <iomanip>
#include <iostream>
#include "Python.h" // Py_Initialize Py_Finalize
#include "pointer2ndarray_api.h" // import_pointer2ndarray ptsetDataSource
int main() {
using namespace std;
cout << "initialize python interpreter" << endl;
Py_Initialize();
cout << "import pointer2ndarray module and initialize C API" << endl;
if (import_pointer2ndarray() < 0) {
cerr << "import pointer2ndarray failed\n";
return 1;
}
cout << "call ptsetDataSource" << endl;
double data[] = {1,2,3};
cout << boolalpha << ptsetDataSource(data, sizeof(data)/sizeof(data[0]))
<< endl;
cout << "shutdown the interpreter" << endl;
Py_Finalize();
cout << "The End" << endl;
}
# call C API function defined in pointer2ndarray.so
from ctypes import cdll, CFUNCTYPE, POINTER, c_longlong, c_bool, c_double
import pointer2ndarray # required
c_double_p = POINTER(c_double)
prototype = CFUNCTYPE(c_bool, c_double_p, c_longlong)
lib = cdll.LoadLibrary(pointer2ndarray.__file__)
for i, name in enumerate(pointer2ndarray.__pyx_capi__):
if "ptsetDataSource" not in name: continue
f = prototype((name, lib))
def errcheck(result, func, arguments):
if not result: raise RuntimeError("%s %s" % (name, arguments))
f.errcheck = errcheck
ary = (c_double*2)(i, .25)
f(ary, len(ary))
# call C API function with a numpy array
import numpy as np
npary = np.array([[11, 12, 13],[21,22,23]], dtype=np.float)
a = np.ascontiguousarray(npary[:,1])
f(a.ctypes.data_as(c_double_p), a.shape[0])
# call python function defined in the extension module
from pointer2ndarray import call_ptsetDataSource
call_ptsetDataSource(npary[:,1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment