Imagine there are two functions with the same signature dog_noise and goose_noise, and goose_noise needs to set up a Honk Apparatus but dog_noise does not, it can easily Bark without prior setup.
Now suppose we want to use our own make_noise_six_times function, but we're going to pass in a function to say which noise. make_noise_six_times(dog_noise) and make_noise_six_times(goose_noise)
With this function pointer approach, make_noise_six_times has no idea about the Honk Apparatus, it will just call into goose_noise six times, each time setting up and tearing down a Honk Apparatus. At runtime these are likely CALL instructions.
However, in a language like Rust that's going to be mono-morphized, make_noise_six_times(dog_noise) and make_noise_six_times(goose_noise) end up generating two implementations which get optimised, there's a good chance the noise sub-functions are inlined - so no function calls - and the creation of the Honk Apparatus may get hoisted out of the loop for the make_noise_six_times(goose_noise) implementation, even though it's across function boundaries, so long as that obeys the "As if" rule.
The reduction in overhead can be dramatic - if your inner functions are tiny the call overhead might dwarf what they actually do, so the inlining makes the whole program orders of magnitude faster in this case. This is very noticeable for e.g. sorting, since the comparison function is executed so often in a hot loop, if that's a C function call it's so much more expensive than if it's a single inlined CPU instruction.
What I'd love is a language which is able to compile 'impl TraitName' into dynamic dispatch in debug mode and only monomorphize it in release mode.
Basically, an automation of this technique: https://play.rust-lang.org/?version=stable&mode=debug&editio...
I suppose the : operator from lua would be useful.
int(* fnptr_name)(int param) becomes typeof(int(int param))* fnptr_name
There is a recent proposal to update standard headers to this style: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3450.txt
-void (*signal(int sig, void (*func)(int)))(int);
+typeof(void (int)) *signal(int sig, typeof(void (int)) *func);
Much better honestly. The original took me a few reads to understand, until I noticed the outer (int) is part of the return type.For the most common use cases C syntax is quite ergonomic once you've learned the principle.
// declare func as normal function
void func(ARGUMENTS);
// declare (*func) as normal function: func is a pointer to a normal function
void (*func)(ARGUMENTS);
// can also declare a type name instead of a function
typedef void functype(ARGUMENTS);
// and then use that to declare function-pointer
functype *func;
// can also declare a function pointer type directly (I don't normally do this)
typedef void (*funcptrtype)(ARGUMENTS);
funcptrtype func; // declare function pointer