This document describes some preliminary ideas about how to
support ctypes within numba. The intention is to support
ctypes function calls directly at the machine code level,
with native performance.

Motivation
==========

One concept which this would enable is easy definition of
"OpenGL ufuncs", with a usage something like:

    @numba.vectorize
    def gl_arrow_ufunc(originx, originy, offsetx, offsety):
        # Call glVertex3f, a ctypes function, several times to define
        # the triangles for the arrow

    origin = np.array(...) # Nx2 array of arrow starts
    offset = np.array(...) # Nx2 array of arrow offsets
    glBegin(GL_TRIANGLES)
    gl_arrow_ufunc(origin[...,0], origin[...,1], offset[...,0], offset[...,1])
    glEnd()

The PyOpenGL library is built using ctypes, which is why this
particular idea works very naturally. Here is what the glVertex3f
function looks like:

    >>> from OpenGL.GL import glVertex3f
    >>> glVertex3f
    <WinFunctionType object at 0x000000000518E1E8>
    >>> repr(glVertex3f.restype)
    'None'
    >>> repr(glVertex3f.argtypes)
    "(<class 'ctypes.c_float'>, <class 'ctypes.c_float'>, <class 'ctypes.c_float'>)"


The idea of being able to dynamically create a numba JIT
ufunc which calls arbitrary ctypes function pointers at
native performance has many more applications.

Design Notes
============

The ctypes libraries allows functions to be called with or without
specifying the 'restype' and 'argtypes' members of the function
pointer. Restricting numba's support to require these fields is
attractive for a few reasons.

One is reliability, since a segfault due to incorrect function
prototype will likely not be directly traceable
back through the numba JIT process to the particular Python source
code line that called the ctypes function.

Another is having additional type information that can be used for
type inference. A type inference pass could infer types backwards
from each ctypes function call point, allowing for a larger number
of numba programs to compile easily.

Implementation Details
======================

The ctypes library doesn't appear to expose the actual function
pointer to Python, but at a C level, the main object starts as follows:

    typedef struct {
        /* First part identical to tagCDataObject */
        PyObject_HEAD
        char *b_ptr;                /* pointer to memory block */

The function pointer itself is retrieved in the function PyCFuncPtr_call
inside _ctypes.c as follows:

    pProc = *(void **)self->b_ptr;

It seems reasonable that numba would access the function pointer
address in the same way through a C-compiled module helper.

The code which maps a ctypes function call in python to a native
function call in LLVM bitcode needs to translate the 'restype' and
'argtypes', along with the calling convention specified by the
PyCFuncPtrObject, into an LLVM bitcode function prototype, and bind
the function pointer address to a variable or extern definition
in the LLVM JIT engine context. Finally, it needs to insert
the function call itself while generating LLVM IR from the Python
bytecode.
