Showing posts with label keywords. Show all posts
Showing posts with label keywords. Show all posts

Friday, 16 December 2011

C++11: Lambda Expressions

Posts in this series
Getting started with C++11
C++11: Initializer lists and range-for statements

So what is a lambda expression?
A lambda expression is way in which to write a function in-line in your code, the typical use case is where you call a function which expects a pointer to another function in order to tailor it to your own needs.  For example, if you had a list of integers (4, 1, 6, 2, 13) and you wanted them sorting, you would typically call a sort method, passing it your list of integers.  Now that sort method may accept a pointer to another function in which you can specify how your list is to be sorted, typically this means that you would have one function defining how to sort in an ascending manner, and another defining a descending manner.  Lets start with an example as to how you would currently do this.
  • bool SortAscending(const int x, const int y)
  • {
  •     return x < y;
  • }
  • ...
  • vector<int> myList = { 4, 1, 6, 2, 13 };
  • sort(myList.begin(), myList.end(), SortAscending); // 1, 2, 4, 6, 13
Full example code

So now, if we wanted to sort this in a descending manner then we would need to write a new method to tell the sort algorithm how to sort the values and pass this to the sort method.

This is a really simple function so why do we have to write a whole method for it?  Well using lambda expressions we no longer have to, with the above being written as follows instead.
  • vector<int> myList = { 4, 1, 6, 2, 13 };
  • sort(myList.begin(), myList.end(), [](int x, int y) -> bool { return x < y; }); // 1, 2, 4, 6, 13
Full example code|

So what did we do here?  Well lets have a look at the lambda expression.
  • [](int x, int y) -> bool { return x < y; }
The opening brackets "[]" is a capture list, more on this later.  Next we define the expressions parameter list "(int x, int y)" just as we would if we were defining a normal method.

The next part is the return type "-> bool", this is an optional part of the expression and we could have easily left it out as the compiler can easily determine from the expression body that the return type is a bool; if the return type is not specified and the expression body does not return any value then a return type of void is deduced.  It is useful, however, to specify a return type if you want to explicitly instruct the compiler what the type being returned should be.

Finally there is the expression body which is every between the "{}" braces.

Capture lists
Sometimes when we use lambda expressions we may want to use or modify values from the surrounding code.  Normally we would just pass these in using a parameter, but with lambda expressions we have to provide a parameter list which the receiving method is expecting, in the case of the sort algorithm method, it is expecting an expression which takes two integers and returns a bool.

To access these surrounding values we can use capture lists.  You've already seen a simple use of a capture list in the previous example where we wrote "[]", this tells the compiler that we are not capturing any values for use in the lambda expression.  In this next example we are going to capture an integer variable called "stepCount" which will be incremented each time our lambda expression is used by the sort algorithm, this will tell us how many times the expression was used to completely sort our list.
  • int stepCount = 0;
  • sort(myList.begin(), myList.end(), [&](int x, int y) -> bool { ++stepCount; return x < y; }); // stepCount is 9
Full example code

The capture list this time is written as "[&]", this instructs the compiler to capture any local variables used in the lambda expression and pass-by-reference.  This allows us to have the sort method update a local variable in our calling code.  Another option for the capture list is "[=]" which tells the compiler to pass-by-value.  If we were to use this in our code however we would get a compiler error as any values passed by value are read-only.

There are other options available for capture lists, these are as follows:

[] Capture nothing
[&] Capture variables by reference
[=] Capture variables by value (make a copy)
[=,&foo] Capture foo by reference, all other variable by value
[foo] Only capture foo and do so by value
[this] Capture the this pointer of the enclosing class

One thing to be careful of here however is that if you return a lambda expression from a method (we'll see how in a second) and you are using a local variable in that method and capturing by reference then you are going to run into problems as the moment you return the expression the local variable you captured as gone out of scope.  Other than that hopefully you can see just how useful these are by now.

Accepting lambda expressions in my own code
One of the great new features in C++11 is the std::function type (and std::bind which I'll cover in a later post) which is a great way for us to start passing around lambda expressions as parameters or return types; in fact it allows us to use lambda expressions and functions.  The structure of the type is std::function<return_type (parameter list)>, so if we wanted to write a method that accepted a lambda expression we could write the following.
  • double Sum(const vector<double>& values, function<double (double x)> f)
  • {
  •     double result = 0.0;
  •     for (auto d : values)
  •     {
  •         result += f(d);
  •     }
  •     return result;
  • }
The "f" parameter is a function which takes a single double parameter and which returns a double value, this is then used in the method body as part of the accumulation process.  The method can then be used as follows.
  • double Complex(double x)
  • {
  •     double result = sin(x * 3.14159265);
  •     result -= floor(result);
  •     
  •     return result;
  • }
  • int main(int argc, char** argv)
  • {
  •     vector myValues = { 1.3, 2.1, 7.4, 9.6 };
  •     
  •     double result1 = Sum(myValues, [](double x) { return x; }); // 20.4
  •     double result2 = Sum(myValues, Complex); // 0.597887
  • }
Full example code

Here we're calling the "Sum" method twice, the first time with a lambda expression which simply returns the value passed into it so that we sum all of the values.  The second call passes a reference to the "Complex" method, which may not be that complex but illustrates the point about being able to pass standard methods as well as lambda expressions.

Lambda expressions provide an easy and convenient method of providing short functions to other other functions which require thema.  Even if you're not sure if you want to use lambda expressions, making sure that your methods use the std::function type means that you can carry on writing helper functions if you want to but that you or others have the option of using lambda expressions if they want to.

For the next post I should be looking at smart pointers, these give us the benefits of pointers in C++ but also provide better memory management, which can only be a good thing

References
CProgramming.com - Lambda Expressions in C++ - the definitive guide
Bjarne Stroustrup - C++11 FAQ

All code provided in this article is provided under a BSD license.  If you spot an error then please do let me know so that we can make this better for anyone else reading it.
Creative Commons Licence
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Monday, 21 November 2011

The var keyword, or what I meant to say

Whilst I'm waiting for something to compile (and for baby number 2 to turn up) I thought I'd post about my thoughts on the "var" keyword in C#.

In previous posts you might have seen that I was somewhat enthusiastic about the "auto" keyword in C++, so it might be natural to assume that I'd feel the same about the "var" keyword in C#.  This is rather unfortunately not the case, allow me to expand a little as to why my feelings about the same functionality in two different languages are not strictly exactly the same.

I'll do it tomorrow, honest!
I'm sort of afraid a little that I'm going to start upsetting people here, so I shall start by saying this.  I am generalizing here and not stereotyping, not everyone is the same and you may indeed be an exception to the rule, but this is generally what I find to be true.

C++ programmers tend to be less lazy than their C# counterparts.  What do I mean by this?  Well, normally when I'm looking at code written by a C++ programmer (in any language) it tends to be easier to read and maintain.  It's because C++ can be painful enough without having to add extra complexity or obfuscation.  Code written by C# programmers tends to be a little lazier, things need tidying up here, stuff is left lying around over there and it generally has that "I'll do it tomorrow" kind of feel to it.

Now don't get me wrong, there is a lot of very nicely written C# code out there, but I tend to find that the people who have written it come from a C/C++ background or have a lot of experience with those or similar languages.

So why should this matter?  Well, when I think of C++ programmers using the "auto" keyword I tend to think of code coming out looking like this:

  • map<int, vector<string>> MyFunction() { ... }
  • void SomeOtherFunction()
  • {
  •     auto result = MyFunction();
  • }

Which is easy to follow, I know when I look in the "SomeOtherFunction" code that I just need to find the "MyFunction" method to see what the type will be (or use the functionality of the IDE), and importantly I know what the code is trying to do without looking this information up.  When I think of C# developers using the "var" keyword then I tend to think of code coming out looking like this (and I have seen this):

  • void MyFunction()
  • {
  •     var a = 1;
  •     var b = 2;
  •     var c = "Something";
  •     ...
  •     var x = a + b;
  • }

Which, okay, is readable and I can make out what is happening but I no longer have clue about the intent of the code; is "a" meant to be a short, an int or a long, maybe it should have been a double?  We could have put some modifiers in there, but that's still not as easy to read.  I just know that if a C++ programmer had written it that we'd have some types in there and the intent would become obvious.  And this isn't just me worrying about something that probably wont happen, I've seen numerous people write code in this way.

What I meant was...
The thing is that the intent of the code is about as important as the code itself.  If I say that a variable is a 64bit integer then it means that I'm expecting some pretty big values in there, similarly if I proclaimed it to be a 16bit integer then I'm expecting very small values.  This kind of information can be invaluable to a maintainer, who might not be some unknown person looking at the code 5 years after you've written it, it might be you after you've spent 2 weeks on a different project and can't quite remember why you wrote something a specific way.

So is "var" a good thing?  Well I would say it is, but like most things it should be used responsibly and never at the cost of losing the intent of the what you are trying to write.  If you're not sure about it, then talk to someone about it, or write the code the way you want and give it to someone who hasn't seen it and ask them if they know what it's trying to do.  If they pull a face then change it, if they know what the intent of the code is without asking too many (what you would consider) obvious questions then it's good to go.

Creative Commons Licence
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.