Exceptions in C¶
Exception handling in C doesn't exist. What's used instead to signify something went wrong isn't set in stone but usually consists of returning an invalid error value (such as a negative number for something that can't normally produce negative numbers) and hoping whoever uses your code remembers to check for them. Unfortunately, this is effectively what we'll have to do too.
But since we're in Python we can provide wrapper functions which turn our flimsy error codes into proper Python exceptions which don't need to be continuously tested for. Before we do that, however, we need to communicate back to Python that something has gone wrong. This can be easy or very fiddly.
If your function currently returns nothing then you can just get it to return an
is ok* boolean.
If you have more than one way a function can go wrong then you may wish to
use a cslug.Header
to define and share a series of error codes for each case.
If the function does already return something then you'll have to use the
return multiple values from a function
trick to give it a second return variable.
The following is an example that shows all the above, using an
automatically generated header
file to share status
constants and ctypes.byref()
to provide both a status output as well as
the intended output of a function.
#include <math.h>
#include "status-codes.h"
int pedantic_log(double in, double * out) {
if (in == 0) return LOG_OF_0_ERROR;
if (in < 0) return LOG_OF_NEGATIVE_VALUE_ERROR;
*out = log(in);
return OK;
}
import enum
import ctypes
from cslug import CSlug, Header
class Status(enum.Enum):
"""Status codes to indicate different error types."""
OK = enum.auto()
LOG_OF_0_ERROR = enum.auto()
LOG_OF_NEGATIVE_VALUE_ERROR = enum.auto()
slug = CSlug("pedantic-log.c", headers=Header("status-codes.h", defines=Status))
def log(x):
"""Fussy natural logarithm which raises exceptions for invalid inputs."""
out = ctypes.c_double()
status = Status(slug.dll.pedantic_log(x, ctypes.byref(out)))
if status is not Status.OK:
# If you're feeling especially motivated you could write a custom error
# message for each outcome - I am not feeling such a motivation...
error = status.name.replace("_", " ").lower()
raise ValueError(f"log({x}) triggered a {error}.")
return out.value