Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anonymous functions using GCC statement expressions

This question isn't terribly specific; it's really for my own C enrichment and I hope others can find it useful as well.

Disclaimer: I know many will have the impulse to respond with "if you're trying to do FP then just use a functional language". I work in an embedded environment that needs to link to many other C libraries, and doesn't have much space for many more large shared libs and does not support many language runtimes. Moreover, dynamic memory allocation is out of the question. I'm also just really curious.

Many of us have seen this nifty C macro for lambda expressions:

#define lambda(return_type, function_body) \
({ \
      return_type __fn__ function_body \
          __fn__; \
})

And an example usage is:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
max(4, 5); // Example

Using gcc -std=c89 -E test.c, the lambda expands to:

int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });

So, these are my questions:

  1. What precisely does the line int (*X); declare? Of course, int * X; is a pointer to an integer, but how do these two differ?

  2. Taking a look at the exapnded macro, what on earth does the final __fn__ do? If I write a test function void test() { printf("hello"); } test; - that immediately throws an error. I do not understand that syntax.

  3. What does this mean for debugging? (I'm planning to experiment myself with this and gdb, but others' experiences or opinions would be great). Would this screw up static analyzers?

like image 625
Bill VB Avatar asked May 01 '12 22:05

Bill VB


People also ask

Which expression creates anonymous functions?

Java supports anonymous functions, named Lambda Expressions, starting with JDK 8. A lambda expression consists of a comma separated list of the formal parameters enclosed in parentheses, an arrow token ( -> ), and a body.

How do you write an anonymous function?

The () makes the anonymous function an expression that returns a function object. An anonymous function is not accessible after its initial creation. Therefore, you often need to assign it to a variable. In this example, the anonymous function has no name between the function keyword and parentheses () .

Is a function expression An anonymous function?

A function expression is an expression which defines a function. Function expressions can be used to define a named or unnamed (anonymous) function. If you define a named function in an expression the name will only be local to the function itself.

How and when do you use anonymous functions?

Anonymous functions are often arguments being passed to higher-order functions, or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function.


3 Answers

This declaration (at block scope):

int (*max)(int, int) =     ({     int __fn__ (int x, int y) { return x > y ? x : y; }     __fn__;     }); 

is not C but is valid GNU C.

It makes use of two gcc extensions:

  1. nested functions
  2. statement expressions

Both nested functions (defining a function inside a compound statement) and statement expressions (({}), basically a block that yields a value) are not permitted in C and come from GNU C.

In a statement expression, the last expression statement is the value of the construct. This is why the nested function __fn__ appears as an expression statement at the end of the statement expression. A function designator (__fn__ in the last expression statement) in a expression is converted to a pointer to a function by the usual conversions. This is the value used to initialize the function pointer max.

like image 154
ouah Avatar answered Oct 05 '22 23:10

ouah


Your lambda macro exploits two funky features. First it uses nested functions to actually define the body of your function (so your lambda is not really anonymous, it just uses an implicit __fn__ variable (which should be renamed to something else, as double-leading-underscore names are reserved for the compiler, so maybe something like yourapp__fn__ would be better).

All of this is itself performed within a GCC compound statement (see http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs), the basic format of which goes something like:

({ ...; retval; }) 

the last statement of the compound statement being the address of the just-declared function. Now, int (*max)(int,int) simply gets assigned the value of the compound statement, which is now the pointer to the 'anonymous' function just declared.

Debugging macros are a royal pain of course.

As for the reason why test; .. at least here, i get the 'test redeclared as different type of symbol', which I assume means GCC is treating it as a declaration and not a (useless) expression. Because untyped variables default to int and because you have already declared test as a function (essentially, void (*)(void)) you get that.. but I could be wrong about that.

This is not portable by any stretch of the imagination though.

like image 44
Mark Nunberg Avatar answered Oct 06 '22 01:10

Mark Nunberg


Partial answer: It isn't int(*X) you are interested in. It is int (*X)(y,z). That is a function pointer to the function called X which takes (y,z) and returns int.

For debugging, this will be really hard. Most debuggers can't trace through a macro. You would most likely have to debug the assembly.

like image 30
Steve Rowe Avatar answered Oct 05 '22 23:10

Steve Rowe