Why would someone want to use a nested function, practically speaking?
When you want to use lambdas, but your language doesn't have lambdas, so you reach for the nearest thing instead.
Say to strictly enforce modularity, e.g helper functions that can only be accessed within its function.
Pascal supports it (at least Turbo Pascal, no idea about ISO Pascal).
For the non-capturing case: mainly to improve readability by allowing utility functions to be defined close to where they are used and with short names.
For the capturing case: to access context that is not available through global variables or function arguments, i.e., the same reason why closures are useful in other languages.
Here's an example, where I have a list of points that I want to sort based on distance to a chosen target point. I can use qsort() which takes an arbitrary comparison function, but has no way to provide context to that function beyond the input arguments:
#include <stdio.h>
#include <stdlib.h>
int main() {
struct Point {
int x, y;
} points[3] = {
{ 3, 1 },
{ 2, 2 },
{ 5, 7 } };
struct Point target = { 4, 5 };
long dsq(const struct Point *p) {
long dx = p->x - target.x, dy = p->y - target.y;
return dx*dx + dy*dy;
}
int compare(const void *p, const void *q) {
long a = dsq(p), b = dsq(q);
return (a > b) - (a < b);
}
qsort(points, 3, sizeof(struct Point), compare);
for (int i = 0; i < 3; ++i) {
printf("%d,%d\n", points[i].x, points[i].y);
}
}
Note here that dsq() is a local function that accesses the `target` variable in the local function scope.The usual workaround in standard C is to pass the necessary context as a function argument. That's why qsort_r() exists, which takes a context argument to be passed to compare(), but that's a non-standard GNU extension.
This practice of passing context pointers around is ubiquitous in C code, and it works, but it can get messy especially if you need access to multiple variables or variables from more than one nested scope. There is also a type safety issue: these context pointers are necessarily passed as void* which means they have to be cast back to the real type before use, which is where bugs can be introduced if the caller and receiver disagree on the actual type.
RAII style cleanup e.g. no gotos
Just a little cleaner than placing it in the global or file namespaces.
So that you can name a section of code without polluting the namespace.
I use them all the time. It's one of the nicest and cleanest features of D. It's an elegant way of:
1. grouping together strongly related functions that are implicitly private to the enclosing function
2. obviating the need to create a struct in order to pass common context to multiple functions
For an example, here's a tree walking function that uses a nested function for the recursion:
private void unrollWalker(elem* e, uint defnum, Symbol* v, targ_llong increment, int unrolls) nothrow
{
int state = 0;
/***********************************
* Walk e in execution order, fixing it according to state.
* state == 0..unrolls-1: when eincrement is found, remove it, advance to next state
* state == 1..unrolls-1: replacing instances of v with v+(state*increment),
* state == unrolls-1: leave eincrement alone, advance to next state
* state == unrolls: done
*/
void walker(elem* e) @trusted
{
assert(e);
const op = e.Eoper;
if (ERTOL(e))
{
if (e.Edef != defnum)
{
walker(e.E2); // this function is @trusted because of this union access
walker(e.E1);
}
}
else if (OTbinary(op))
{
if (e.Edef != defnum)
{
walker(e.E1);
walker(e.E2);
}
}
else if (OTunary(op))
{
assert(e.Edef != defnum);
walker(e.E1);
}
else if (op == OPvar &&
state &&
e.Vsym == v)
{
// overwrite e with (v+increment)
elem* e1 = el_calloc();
el_copy(e1,e);
e.Eoper = OPadd;
e.E1 = e1;
e.E2 = el_long(e.Ety, increment * state);
}
if (OTdef(op) && e.Edef == defnum)
{
// found the increment elem; neuter all but the last one
if (state + 1 < unrolls)
{
el_free(e.E1);
el_free(e.E2);
e.Eoper = OPconst;
e.Vllong = 0;
}
++state;
}
}
walker(e);
assert(state == unrolls);
}
Only one argument needs to be passed to walker(), because the other context data is accessible from the enclosing function.https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ba...
Encapsulation
Good C style is that every function that accepts a callback should also accept an opaque context pointer it then passes through unchanged to the callback. Usually the caller will allocate a structure on the stack or the heap, stash some of its local variables there, then use them in the callback. A nested function does the structure back-and-forth for you in the stack-allocated case. In GCC’s original formulation it also passes the context pointer implicitly
but that requires an executable stack and TFA is about avoiding that part.