60

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?

royhowie
  • 11,075
  • 14
  • 50
  • 67
Bill
  • 2,319
  • 9
  • 29
  • 36
  • Also, I don't see `int (*X);` anywhere. – Oliver Charlesworth May 01 '12 at 22:49
  • It's not in there as such. While trying to figure out the syntax, I was a little stumped that int (*X); compiles, but not really sure what it defined.. – Bill May 01 '12 at 22:51
  • 3
    `int (*X)();` is a prototype for a function pointer by the name of `X`. In other words: `X` is a pointer to a function accepting `` and returning a `int`. – pmg May 01 '12 at 22:52
  • So, let's say we just have `int (*x)` - this is a pointer to a function with... a variable list of arguments, no arguments? – Bill May 01 '12 at 22:54
  • And how is this not ANSI C? By convention? because it compiles under -std=c89 – Bill May 01 '12 at 22:55
  • 2
    To make gcc a strict C89 (or C99) compiler, use `-std=c89 -pedantic`. – pmg May 01 '12 at 22:56
  • @B.VB.: Because the mechanism here (statement expressions) are not in the C standard(s). – Oliver Charlesworth May 01 '12 at 22:57
  • @B.VB. You need the `()`s because otherwise you end up forward-declaring `X` as a function returning a pointer to `int`. – Neil May 01 '12 at 23:15
  • @OliCharlesworth: Also, nested functions come into play, which are a GCC extension. – Ed S. May 01 '12 at 23:22
  • @OliverCharlesworth, So how do we do anonymous functions in ANSI C? – Pacerier Mar 06 '15 at 11:03
  • @pacerier: we don't. Simply not supported. – Oliver Charlesworth Mar 06 '15 at 12:39
  • @B.VB. `int (*x)` is equivalent to `int *x`, i.e. a pointer (`*`) to int. `int *x()` is a function returning a pointer to int. Notice the problem here: the parameter-list declarator (`()`) has higher operator precedence than the pointer (`*`). To override this, use parens: `int (*x)()` is a pointer (`*`) to a function (`()`) taking unspecified arguments, returning int. http://cdecl.org/ – Braden Best Feb 04 '17 at 12:12

4 Answers4

45

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.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • 4
    Such a useful functionality, When will this be implemented into ANSI C? – Pacerier Mar 06 '15 at 11:03
  • 3
    It is syntactically valid GNU C, but is it really semantically valid? When the block containing the function definition exits (which is before the any actual use of the "lambda expression" can occur), wouldn't the function trampoline be invalidated along with it? – Dolda2000 Aug 09 '16 at 16:37
  • 1
    @Dolda2000 Data created in a statement expression probably has static duration. After all, it's no longer ANSI C. It's GNU C. GNU defines that standard. – Braden Best Feb 04 '17 at 12:22
  • 2
    @BradenBest: It certainly does not have static duration, because a pointer to a nested function is invalid after the containing function returns. Since a nested function has access to all variables that are lexically in its scope, it wouldn't be strange if its trampoline is allocated with the same duration as those variables. The manual is a bit unclear on the issue, though. – Dolda2000 Feb 10 '17 at 03:27
  • @Pacerier statement expression is supported by gcc and clang so it's quite portable and you can safely use it (even linux kernel code uses it) – SwiftMango Nov 23 '17 at 15:57
  • @ouah can you please have a look at this similar question, the difference is that I'm using malloc and have part of the code in a library and the other part in user of the library. Thanks in advance.. https://stackoverflow.com/questions/48471963/anonymous-functions-return-dynamically-allocated-values – Developer Jan 27 '18 at 08:55
7

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.

Mark Nunberg
  • 3,551
  • 15
  • 18
1

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.

Steve Rowe
  • 19,411
  • 9
  • 51
  • 82
0
  1. int (*max)(int, int) is the type of variable you are declaring. It is defined as a function pointer named max which returns int, and takes two ints as parameters.

  2. __fn__ refers to the function name, which in this case is max.

  3. I don't have an answer there. I would imagine you can step through it if you have run it through the preprocessor.

gcochard
  • 11,408
  • 1
  • 26
  • 41
  • wrt 2, I just don't understand why does `int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });` compile, but `void test() { puts("hello"); } test;` *not* compile? – Bill May 01 '12 at 22:53
  • @B.VB. I would make the code look exactly alike. `void (*test)() = ({ void test() {puts("hello");} test; });` or if that doesn't work, `void (*test)() = ({ void __fn__() { puts("hello");} __fn__; });` – gcochard May 01 '12 at 22:57
  • 1
    `__fn__` is just an arbitrary name for the function defined inside the block. The naming is unfortunately confusing. – FooF Aug 15 '12 at 05:41