Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ shorter lambda syntax

Tags:

c++

c++11

lambda

I'm new to C++, coming recently from Swift. Is there any way to get shorter lambda syntax?

I have a lot of lines like:

columns = {
    Col(_("Name"), "string", [] (Person *p) {
                                return p->Name();
                              }),
    Col(_("Age"), "int", [] (Person *p) {
                                return p->Age();
                              }),
    Col(_("Bank"), "string", [&banks] (Person *p) {
                            return banks.lookUp(p->Identifier()).Name;
                           }),
    //..etc..
};

Some of the columns require longer lambdas, but as it is the syntax for writing the lambda is about as long as the content it self.

Can the lambda syntax be reduced at all? (say by implicit argument or implicitly return the last statement)

Eg in Swift I could do something like this and it would be the same:

    Col(_("Age"), "int", { $0.Age() }),

EDIT: Added the Bank column as an example of a more complicated one.

like image 398
Jonathan. Avatar asked Apr 21 '16 16:04

Jonathan.


People also ask

What is the correct syntax for lambda expression in C++11?

Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };

Does C have lambda expression?

No, C doesn't have lambda expressions (or any other way to create closures). This is likely so because C is a low-level language that avoids features that might have bad performance and/or make the language or run-time system more complex.

What is the syntax of defining lambda expression?

A lambda expression is an anonymous function that provides a concise and functional syntax, which is used to write anonymous methods. It is based on the function programming concept and used to create delegates or expression tree types. The syntax is function(arg1, arg2... argn) expression.

How do you write lambda in C++?

Creating a Lambda Expression in C++auto greet = []() { // lambda function body }; Here, [] is called the lambda introducer which denotes the start of the lambda expression. () is called the parameter list which is similar to the () operator of a normal function.


6 Answers

I made a terse lambda library to do this with a macro, provided that you can use C++20 (v0.1.1 supports C++17 with painful caveats). With this library, your code would be:

columns = {
    Col(_("Name"), "string", [] TL(_1->Name())),
    Col(_("Age"), "int", [] TL(_1->Age())),
    Col(_("Bank"), "string", [&banks] TL(banks.lookUp(_1->Identifier()))),
    //..etc..
};

This TL macro gives you an expression lambda similar to the { $0.Age() } Swift syntax, letting you access parameters with _1, _2, etc. Furthermore, this lambda is SFINAE-friendly and noexcept-friendly for those situations where that's desirable.

Note that TL's lambda returns by value like you did in your sample lambdas. If you wanted a decltype(auto) return type for your lambda, you can use the TLR macro instead.


I recommend caution if you want to use this library; using macros to alter the syntax of the language is a dangerous idea and can make your code hard to read.

like image 193
Justin Avatar answered Oct 21 '22 23:10

Justin


If you're always calling a member function, you can use mem_fn:

Col(_("Name"), "string", std::mem_fn(&Person::Name)),

A mem_fn works when passed either a pointer or a reference to the object type.

like image 40
ecatmur Avatar answered Oct 21 '22 23:10

ecatmur


Can the lambda syntax be reduced at all?

I don't think so. The essential components of a lambda function for your needs are:

[ capture-list ] ( params ) { body }

You have it in as minimal a form as is possible with C++11.

like image 39
R Sahu Avatar answered Oct 21 '22 21:10

R Sahu


Using C++14 and macros, you can closely mimick syntax of arrow functions from javascript (ES6).

In fact, it is usually enough to capture everything by reference. Argument type can be set to auto, return type can be omitted. So the shortest version that C++14 allows us is:

auto f = [&](auto x, auto y) { return x + y; };

Clearly, it can be easily turned into macros:

#define _L2(a, b, res) [&](auto a, auto b) { return res; }
#define _S2(res) _L2(_0, _1, res)

The _L2 macro creates a lambda with two user-specified parameters. The _S2 macro creates a lambda with parameters named _0 and _1. Lastly, we can use this answer to overload macro _L by number of arguments. I have no idea how to deduce number of arguments for _S via macros.

Now we can write something like:

auto g = _L(x, y, x + y);    //std::plus
auto g = _S2(_0 + _1);       //std::plus

You can even do some crazy things like:

//turns unary accessor function into binary comparator functor
auto compByKey = _L(f, _L(x, y, f(x) < f(y)));
//sort vector<string> by length
sort(arr.begin(), arr.end(), compByKey(_L(x, x.length())));

Indeed, the syntax is still not as clear as in the original javascript or swift, but it is much shorter. The problem now is to remember all the kinds of lambdas we have defined =) Anyway, STL library is not friendly to functional style programming...

Full code is available on ideone.

like image 24
stgatilov Avatar answered Oct 21 '22 21:10

stgatilov


Instead of passing a lambda at all, you should rework Col_ so that it can accept pointers to members and will know what to do with them (e.g. by wrapping them in std::mem_fn). That way you can just write:

columns = {
    Col(_("Name"), "string", &Person::Name),
    Col(_("Age"), "int", &Person::Age),
    //..etc..
};
like image 36
Barry Avatar answered Oct 21 '22 21:10

Barry


if you have c++14 (and if you're coming from swift you probably do) then you can replace the argument type with auto. In addition, non-capturing lambdas require no space (they are equivalent to a function pointer) so there is no performance hit if you simply pre-define them and use a copy in your initialiser.

// this will return the Name of any pointee that has a Name() method
auto get_name = [](auto*p) { return p->Name(); }
auto get_age = [](auto*p) { return p->Age(); }

columns = {
    Col(_("Name"), "string", get_name),
    Col(_("Age"), "int", get_age),
    //..etc..
};
like image 44
Richard Hodges Avatar answered Oct 21 '22 21:10

Richard Hodges