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:

multi-output.c
#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)