On 8/23/2020 4:13 AM, Kenny McCormack wrote:
> WARNING: This is all gcc-specific...
>
> I found this code on some website; it does what it claims to do:
>
> #include <stdio.h>
>
> int main(void)
> {
> int (*max)(int, int) =
> ({
> int __fn__ (int x, int y) { return x > y ? x : y; }
> __fn__;
> });
>
> printf("max(-5,5) = %d\n",max(-5,5));
> }
>
> Now, this is cute and all, but how is it any different from just declaring
> max() the usual way? Given that functions and function pointers are
> basically the same thing in C (just like arrays and pointers are basically
> the same thing - yes, yes, I know that technically they are not, but don't
> bother writing in to tell me that), when we write:
>
> somefunction() { ... }
>
> we know that, really, we're just creating a block of code and setting
> somefunction to be the address of that function.
>
> So, again, what is the advantage of the above code?
>
IIRC, the GCC extension also allows the nested function to capture
variables from the parent scope (by reference), effectively serving as a
"lambda" which remains valid until the parent function returns.
IIRC, they fall back to plain functions (with no restrictions on
lifespan) if no variable capture takes place.
Theoretically, these can be useful for callbacks, but their utility is
limited to some extent. For some contexts, it would be more useful to
have a heap-allocated lambda with by-value capture semantics.
However:
This would require a way to free the lambdas (eg: "free()");
This would make sense to have a different syntax;
Implicitly, this assumes/requires that heap memory be executable.
Or, alternately, the C library provides an alternate sub-heap for this;
making the main heap executable poses a security risk (though, similar
goes for allowing the stack to be executable).
C++ has support for lambda's support both by-reference and by-value
semantics, but are handled as a complex value type rather than a plain
function pointer, so are less useful in contexts where one needs a
function pointer.
I guess I could consider adding something like (to my C compiler):
int (*foo)(int x, int y);
int z;
foo = __func__(int x, int y):int { return (x+y)*z; };
Which would essentially allocate the lambda on the heap and capture
things by value (if needed, capture-by-reference could be done indirectly).
In effect, it would create a heap-allocated thunk which loads its own
pointer into a register and then branches to the main function body
(located within the ".text" section as-usual).
The same compiler is shared between C and another language of mine which
has lambdas which use a basically similar mechanism (just, in this
language, one can uses "delete" to free lambda's, well, and/or one can
just leak them into the ether, either way, *1...).
*1: This language also has the semantics that all allocated memory gets
freed when the main heap goes out of scope (eg: the process terminates)
which on most modern targets can be implemented reasonably cheaply...