Function Pointers

Once we introduce variables, symbols will no longer represent functions in our language, but rather they will represent a name for us to look up into our environment and get some new value back from.

Therefore we need a new value to represent functions in our language, which we can return once one of the builtin symbols is encountered. To create this new type of lval we are going to use something called a function pointer.

Function pointers are a great feature of C that lets you store and pass around pointers to functions. It doesn’t make sense to edit the data pointed to by these pointers. Instead we use them to call the function they point to, as if it were a normal function.

Like normal pointers, function pointers have some type associated with them. This type specifies the type of the function pointed to, not the type of the data pointed to. This lets the compiler work out if it has been called correctly.

In the previous chapter our builtin functions took a lval as input and returned a `lvalas output. In this chapter our builtin functions will take an extra pointer to the environmentlenv*as input. We can declare a new function pointer type calledlbuiltin`, for this type of function, like this.

  1. typedef lval*(*lbuiltin)(lenv*, lval*);

Why is that syntax so odd?

In some places the syntax of C can look particularly weird. It can help if we understand exactly why the syntax is like this. Let us de-construct the syntax in the example above part by part. First the typedef. This can be put before any standard variable declaration. It results in the name of the variable, being declared a new type, matching what would be the inferred type of that variable. This is why in the above declaration what looks like the function name becomes the new type name. Next all those *. Pointer types in C are actually meant to be written with the star * on the left hand side of the variable name, not the right hand side of the type int *x;. This is because C type syntax works by a kind of inference. Instead of reading “Create a new int pointer x“. It is meant to read “Create a new variable x where to dereference x results in an int.” Therefore x is inferred to be a pointer to an int. This idea is extended to function pointers. We can read the above declaration as follows. “To get an lval* we dereference lbuiltin and call it with a lenv* and a lval*.” Therefore lbuiltin must be a function pointer that takes an lenv* and a lval* and returns a lval*.