21

I found this piece of code on GitHub but didn't quite understand it:

#define lambda(ret_type, _body) ({ ret_type _ _body _; })

Then:

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

int max_value = max(1, 2);
// max_value is 2

What are the underscores doing inside the #define and how does it return a function pointer?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Kish
  • 293
  • 1
  • 6
  • 8
    Have you tried just expanding the macro (eg. with `gcc -E`) to see what it does? – Useless Jan 30 '20 at 16:23
  • 5
    Please look at the expansion https://godbolt.org/z/C5TLWj The result is not easy to understand though – Eugene Sh. Jan 30 '20 at 16:23
  • First thing to understand with macros is that it will drop in exactly what you have put in and replace your parameters with whatever you supply, so make sure the parameters are wrapped in (), for each one or the results could be undesirable. – SPlatten Jan 30 '20 at 16:26
  • 2
    I assume you know based on the comments around the spot where you got this code, but this does rely on GCC extensions for nested functions. – Thomas Jager Jan 30 '20 at 16:27
  • @ThomasJager Is it? Looks like some kind of function pointer initialized with compound literal of a function if such a thing even exists – Eugene Sh. Jan 30 '20 at 16:27
  • 4
    @EugeneSh. It's initializing a function pointer by using GCC's nested functions. The original code is from [here](https://gist.github.com/shakna-israel/4fd31ee469274aa49f8f9793c3e71163#file-evil_lambda-h-L10). This project was shared on Hacker News today. – Thomas Jager Jan 30 '20 at 16:29
  • In C && C++ this macro does exactly nothing, as it is syntactic wrong and the code will not compile – 0___________ Jan 30 '20 at 16:33
  • @ThomasJager Can you call it "nested function" if it is appearing inside initializer? – Eugene Sh. Jan 30 '20 at 16:34
  • 4
    @EugeneSh. It's a combination of two GCC extensions: nested functions, and [compound statements in expressions](https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html). The nested function appears inside the compound statement. – interjay Jan 30 '20 at 16:35
  • @interjay are you sure? https://godbolt.org/z/BVdmig – 0___________ Jan 30 '20 at 16:36
  • @interjay Yeah, because eventually you can't call `_ (2,3)` from within `main`: https://ideone.com/T5FLXb – Eugene Sh. Jan 30 '20 at 16:37
  • To answer the first part of your question, authors of the macro rely on the fact that underscores are valid identifiers. You can replace both instances of stand-alone underscore `_` with `foobar`, and get the same effect. – Sergey Kalinichenko Jan 30 '20 at 16:42
  • @EugeneSh. Yeah, GCC documentation states "The nested function’s name is local to the block where it is defined". – interjay Jan 30 '20 at 16:44
  • @P__J__ You removed the parantheses from the `lambda` macro, so it no longer counts as GCC's statement expression extension. – interjay Jan 30 '20 at 16:45
  • @interjay it does not matter https://godbolt.org/z/5eKOtA or https://godbolt.org/z/wdMkVT – 0___________ Jan 30 '20 at 16:47
  • 2
    @P__J__ You're compiling as C++ where GCC might not allow nested functions. And as the error message says: "statement-expressions are not allowed outside functions", so you'll have to move the `max` definition into another function. – interjay Jan 30 '20 at 16:50
  • Why does this question has so many upvotes while the answers don´t? – RobertS supports Monica Cellio Jan 30 '20 at 18:06
  • [What is a Lambda function?](https://stackoverflow.com/questions/16501/what-is-a-lambda-function) – RobertS supports Monica Cellio Jan 30 '20 at 18:08
  • 1
    @RobertSsupportsMonicaCellio Good question. The question itself is quite low-grade: "I saw this code, I don't understand it, please explain". While the answer is quite sophisticated. – Eugene Sh. Jan 30 '20 at 18:09
  • @EugeneSh. Are we facing a new phenomenon? Crazy world these days. – RobertS supports Monica Cellio Jan 30 '20 at 18:13
  • @RobertSsupportsMonicaCellio I don't think it is new.. many "canonical" questions with thousands of upvotes are quite silly by todays standards. The recent change to award questions the same points as answers seems unfair given that. – Eugene Sh. Jan 30 '20 at 18:14
  • @EugeneSh. I agree, but I still don´t understand why on this particular question, the answers are voted so badly in comparison to the question. I mean, it is a very good question even if it lacks in research effort and spelling the certain problem but to deprecate good answers is not a thing, I´ve seen before here on SO and IMHO shouldn´t be. – RobertS supports Monica Cellio Jan 30 '20 at 18:21
  • This would be a better question if it linked where the code is from. – Peter Cordes Jan 31 '20 at 01:41

2 Answers2

11

Using this macro,

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

expands to:

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

In the curly braces, this uses GCC's Nested Functions to create a function that performs the desired operation. Within the inner scope, it has the name _.

Then, as noted by interjay, GCC's Statement Expressions are used. Effectively, the function _ is assigned to the pointer max.

If such a macro isn't being used, this could be written differently and used as:

int val1 = 4;
int val2 = -30;

int perform_operation(int (*op)(int, int)) {
    int new_val = op(val1, val2);
    val1 = val2;
    val2 = new_val;
    return new_val;
}

int enclosing_function (void) {
    // Create max "lambda"
    int (*max)(int, int);
    {
        // Curly braces limit the scope of _
        int _ (int x, int y) { return x > y ? x : y; }
        max = _;
    }

    return perform_operation(max);
}

The three methods can be compared in this code example.

Thomas Jager
  • 4,836
  • 2
  • 16
  • 30
8

This is called a statement expression and creates a "lambda" (or nested function) and returns a pointer to it. It is GNU C-specific.

The macro expands to:

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

The _ at the end is like a return.

The underscore is actually the name of the function that is created and "returned". It's used because it's an uncommonly-used identifier (for good reason; _ is quite possibly the least descriptive identifier possible).

The reason the statement expression is used is so _ won't be defined after the scope of the statement expression exits.

So, going through the macro:

#define lambda(ret_type, _body) ({ ret_type _ _body _; })

ret_type is the return type of the "lambda". _ is the name of the function used inside it because it is an uncommon identifier name. _body consists of the arguments and body of the function. The trailing _ "returns" the "lambda".

This code is found at Let's Destroy C (which is an appropriate name). You shouldn't use it. It will make your code work only on compilers that support GNU C extensions. Instead, just write a function or macro.

If you use constructs like this a lot or want more features, I suggest using C++. With C++ you can do something similar to this and have portable code.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76