# Creating C callbacks with `@cfunc`

Interfacing with some native libraries (for example written in C or C++) can necessitate writing native callbacks to provide business logic to the library. The `numba.cfunc()` decorator creates a compiled function callable from foreign C code, using the signature of your choice.

## Basic usage

The `@cfunc` decorator has a similar usage to `@jit`, but with an important difference: passing a single signature is mandatory. It determines the visible signature of the C callback:

    from numba import cfunc

    @cfunc("float64(float64, float64)")
    def add(x, y):
        return x + y

The C function object exposes the address of the compiled C callback as the `address` attribute, so that you can pass it to any foreign C or C++ library. It also exposes a `ctypes` callback object pointing to that callback; that object is also callable from Python, making it easy to check the compiled code:

    @cfunc("float64(float64, float64)")
    def add(x, y):
        return x + y

    print(add.ctypes(4.0, 5.0))  # prints "9.0"

## Example

In this example, we are going to be using the `scipy.integrate.quad` function. That function accepts either a regular Python callback or a C callback wrapped in a `ctypes` callback object.

Let’s define a pure Python integrand and compile it as a C callback:

    >>> import numpy as np
    >>> from numba import cfunc
    >>> def integrand(t):
            return np.exp(-t) / t**2
       ...:
    >>> nb_integrand = cfunc("float64(float64)")(integrand)

We can pass the `nb_integrand` object’s `ctypes` callback to `scipy.integrate.quad` and check that the results are the same as with the pure Python function:

    >>> import scipy.integrate as si
    >>> def do_integrate(func):
            """
            Integrate the given function from 1.0 to +inf.
            """
            return si.quad(func, 1, np.inf)
       ...:
    >>> do_integrate(integrand)
    (0.14849550677592208, 3.8736750296130505e-10)
    >>> do_integrate(nb_integrand.ctypes)
    (0.14849550677592208, 3.8736750296130505e-10)

Using the compiled callback, the integration function does not invoke the Python interpreter each time it evaluates the integrand. In our case, the integration is made 18 times faster:

    >>> %timeit do_integrate(integrand)
    1000 loops, best of 3: 242 µs per loop
    >>> %timeit do_integrate(nb_integrand.ctypes)
    100000 loops, best of 3: 13.5 µs per loop

## Dealing with pointers and array memory

A less trivial use case of C callbacks involves doing operation on some array of data passed by the caller. As C doesn’t have a high-level abstraction similar to Numpy arrays, the C callback’s signature will pass low-level pointer and size arguments. Nevertheless, the Python code for the callback will expect to exploit the power and expressiveness of Numpy arrays.

In the following example, the C callback is expected to operate on 2-d arrays, with the signature `void(double *input, double *output, int m, int n)`. You can implement such a callback thusly:

    from numba import cfunc, types, carray

    c_sig = types.void(types.CPointer(types.double),
                       types.CPointer(types.double),
                       types.intc, types.intc)

    @cfunc(c_sig)
    def my_callback(in_, out, m, n):
        in_array = carray(in_, (m, n))
        out_array = carray(out, (m, n))
        for i in range(m):
            for j in range(n):
                out_array[i, j] = 2 * in_array[i, j]

The `numba.carray()` function takes as input a data pointer and a shape and returns an array view of the given shape over that data. The data is assumed to be laid out in C order. If the data is laid out in Fortran order, `numba.farray()` should be used instead.

## Handling C structures

### With CFFI

For applications that have a lot of state, it is useful to pass data in C structures. To simplify the interoperability with C code, numba can convert a `cffi` type into a numba `Record` type using `numba.core.typing.cffi_utils.map_type`:

    from numba.core.typing import cffi_utils

    nbtype = cffi_utils.map_type(cffi_type, use_record_dtype=True)

Note

use_record_dtype=True is needed otherwise pointers to C structures are returned as void pointers.

Note

From v0.49 the `numba.cffi_support` module has been phased out in favour of `numba.core.typing.cffi_utils`

For example:

    from cffi import FFI

    src = """

    /* Define the C struct */
    typedef struct my_struct {
       int    i1;
       float  f2;
       double d3;
       float  af4[7]; // arrays are supported
    } my_struct;

    /* Define a callback function */
    typedef double (*my_func)(my_struct*, size_t);
    """

    ffi = FFI()
    ffi.cdef(src)

    # Get the function signature from *my_func*
    sig = cffi_utils.map_type(ffi.typeof('my_func'), use_record_dtype=True)

    # Make the cfunc
    from numba import cfunc, carray

    @cfunc(sig)
    def foo(ptr, n):
       base = carray(ptr, n)  # view pointer as an array of my_struct
       tmp = 0
       for i in range(n):
          tmp += base[i].i1 * base[i].f2 / base[i].d3
          tmp += base[i].af4.sum()  # nested arrays are like normal NumPy arrays
       return tmp

### With `numba.types.Record.make_c_struct`

The `numba.types.Record` type can be created manually to follow a C-structure’s layout. To do that, use `Record.make_c_struct`, for example:

    my_struct = types.Record.make_c_struct([
       # Provides a sequence of 2-tuples i.e. (name:str, type:Type)
       ('i1', types.int32),
       ('f2', types.float32),
       ('d3', types.float64),
       ('af4', types.NestedArray(dtype=types.float32, shape=(7,))),
    ])

Due to ABI limitations, structures should be passed as pointers using `types.CPointer(my_struct)` as the argument type. Inside the `cfunc` body, the `my_struct*` can be accessed with `carray`.

### Full example

See full example in `examples/notebooks/Accessing C Struct Data.ipynb`.

## Signature specification

The explicit `@cfunc` signature can use any Numba types, but only a subset of them make sense for a C callback. You should generally limit yourself to scalar types (such as `int8` or `float64`) ,pointers to them (for example `types.CPointer(types.int8)`), or pointers to `Record` type.

## Compilation options

A number of keyword-only arguments can be passed to the `@cfunc` decorator: `nopython` and `cache`. Their meaning is similar to those in the `@jit` decorator.

## Calling C code from Numba

It is also possible to call C code from Numba `@jit` functions. In this example, we are going to be compiling a simple function `sum` that adds two integers and calling it within Numba `@jit` code.

Note

The example below was tested on Linux and will likely work on Unix-like operating systems.

    #include <stdint.h>

    int64_t sum(int64_t a, int64_t b){
       return a + b;
    }

Compile the code with `gcc lib.c -fPIC -shared -o shared_library.so` to generate a shared library.

    from numba import njit
    from numba.core import types, typing
    from llvmlite import binding
    import os

    # load the library into LLVM
    path = os.path.abspath('./shared_library.so')
    binding.load_library_permanently(path)

    # Adds typing information
    c_func_name = 'sum'
    return_type = types.int64
    argty = types.int64
    c_sig = typing.signature(return_type, argty, argty)
    c_func = types.ExternalFunction(c_func_name, c_sig)

    @njit
    def example(x, y):
       return c_func(x, y)

    print(example(3, 4)) # 7

It is also possible to use `ctypes` as well to call C functions. The advantage of using `ctypes` is that it is invariant to the usage of JIT decorators.

    from numba import njit
    import ctypes
    DSO = ctypes.CDLL('./shared_library.so')

    # Add typing information
    c_func = DSO.sum
    c_func.restype = ctypes.c_int
    c_func.argtypes = [ctypes.c_int, ctypes.c_int]

    @njit
    def example(x, y):
       return c_func(x, y)

    print(example(3, 4)) # 7
    print(example.py_func(3, 4)) # 7