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.
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; };
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.
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.
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.
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.
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.
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.
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.
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..
};
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..
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With