Return multiple values from a function¶
In Python, if a function has more than one output you return a tuple. In C, the best you can do is return a struct which is a lot of work for something so simple. Alternatively you can pass arguments by reference which the C function can write to.
For example, the following function has two outputs, even though it doesn't
use a return value. Instead it takes pointer inputs min
and max
which
will be written to:
#include <math.h>
void range_of(double * values, int len_values, double * min, double * max) {
/* Get the minimum and maximum of an array of doubles. */
// Set defaults for the min and max.
*min = INFINITY;
*max = -INFINITY;
// Find the min and max.
for (int i = 0; i < len_values; i++) {
if (values[i] > *max) *max = values[i];
if (values[i] < *min) *min = values[i];
}
} // Our outputs are in fact inputs so we don't return anything.
To use it we the usual setup:
import ctypes
import array
from cslug import CSlug, ptr
slug = CSlug("multi-output.c")
We do have to do some leg-work to interact with this function. Lets give ourselves an input array of the correct type we'd like to use:
values = array.array("d", range(20))
Now to finally run our C code. The ctypes.byref()
calls are where the
magic is happening.
# Create uninitialised `min_` and `max_` values to be written to.
min_, max_ = ctypes.c_double(), ctypes.c_double()
# Use `ctypes.byref()` to pass them to C as writable pointers.
slug.dll.range_of(ptr(values), len(values), ctypes.byref(min_),
ctypes.byref(max_))
Obviously you don't want to go through this every time you use this function so write a wrapper function containing the above. Whilst you're at it, you can incorporate some type normalising.
def range_of(values):
"""
A user friendly wrapper around the raw C ``range_of()`` function.
"""
# Molly-coddle `values` to make sure it's of the right type.
if not (isinstance(values, array.ArrayType) and values.typecode == "d"):
values = array.array("d", values)
# Create uninitialised `min_` and `max_` values to be written to.
min_, max_ = ctypes.c_double(), ctypes.c_double()
# Use `ctypes.byref()` to pass them to C as writable pointers.
slug.dll.range_of(ptr(values), len(values), ctypes.byref(min_),
ctypes.byref(max_))
# Return the contents of `min_` and `max_` as native Python floats.
return min_.value, max_.value
Now you can almost forget that this function is implemented in C:
>>> range_of([6, 9, 3, 13.5, 8.7, -4])
(-4., 13.5)