Fighting FUD – Introducing C++11 to Legacy Programmers


I’m at the ACCU 2013 conference, and this morning Bjarne Stroustrup held a keynote about C++11. One of his points was that full adoption of C++11 will take some time, due to compilers, libraries etc. lagging behind, but also due to many programmers not wanting to use new things in general. What can you do about it? Bjarne briefly suggested we fight the FUD by starting to introduce the very simplest features that just make everyone’s lives easier. Here is my take on that.

Simple feature #1: Uniform Initilaization

Initializing containers used to be painful. For example, initializing a vector of ints required a long list of push_backs, or something like this:

    int tmp_v[] = {1, 1, 2, 3, 5, 8};
    vector<int> old_v(tmp_v, tmp_v + sizeof(tmp_v) / sizeof(tmp_v[0]));

Now however, we can do this:

    vector<int> new_v = {1, 1, 2, 3, 5, 8};

Even the most hard-core “I don’t need all that new stuff” legacy programmer will appreciate that. Btw, I think this is especially useful in unit tests, where you typically initialize a lot of data manually. (Those legacy programmers probably “don’t need all that new unit testing stuff” either, though.)

This becomes even more important when you have more complex containers:

    vector<pair<int, string>> ints = {{1, "one"}, {2, "two"}};

Simple feature #2: Type deduction

Let’s say we want to print out that vector of pairs from the previous example. This is how you’d do it in C++03:

    for (vector<pair<int, string>>::const_iterator it = ints.begin(); it != ints.end(); ++it)
    {
        cout << it->first << ":" << it->second << " ";
    }

That vector<pair<int,string>>::const_iterator is a bit cumbersome, right? Well, in C++11 we can let the compiler figure out the type for us:

    for (auto it = ints.begin(); it != ints.end(); ++it)
    {
        cout << it->first << ":" << it->second << " ";
    }
    cout << endl;

Note that auto is resolved compile-time, this is not dynamic typing. But we can do even better:

Simple feature #3: Range based for

    for (auto& elm : ints)
    {
        cout << elm.first << ":" << elm.second << " ";
    }
    cout << endl;

This really is as simple as it gets. Notice that the type of elm is no longer an iterator, it is a reference to the element. This means you no longer have to dereference the iterator, and things become even simpler. (Not having to dereference is a bigger issue when the containers store pointers, and you end up doing (*it)->member.)

(Not so?) simple feature #4: Lambdas

The first three features should be fairly simple to convince anyone to use. I would however argue that it should be fairly simple to make an argument for the simplest uses of lambda functions too.

Lets say we want to find an element in that vector<pair>. To do that, we need a predicate function/functor. In C++03, we would need to either do something with std::bind_1st / std::bind_2nd, or write our own predicate like this:

    class CompareString
    {
        public:
            CompareString(string s) : s(s) {}
            bool operator()(const pair<int,string>& p) { return p.second == s; };
        private:
            string s;
    };

    auto it = find_if(ints.begin(), ints.end(), CompareString("two"));

We are not really interested in making that class. All we want is the content of operator(). Wouldn’t it be nice if we could just paste that code directly in the call to find_if? Something like this?

    auto it2 = find_if(ints.begin(), ints.end(),
        bool operator()(const pair<int,string>& p) { return p.second == "two";});    

With lambdas, we can:

    auto it2 = find_if(ints.begin(), ints.end(),
        [](const pair<int,string>& p) { return p.second == "two";});    

For the purpose of this article, [] can be read as “lambda function follows:”. Notice that we don’t even have to specify the return type, as it can be deduced by the compiler.

Conclusion

That’s it! Just a few simple things to start introducing in you code base, to fight the FUD and convince the legacy programmers that C++11 is nice. (And sorry if this blog post feels a bit rushed, I wrote it during lunch at ACCU 2013. Now I’m off to see my friend Mike Long talk about legacy code base restoration projects.)

As usual, the code for this blog post is available on GitHub.

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.

Uniform Initialization Simplifies Testing


In which I demonstrate the use of uniform initialization, and show how it is particularly well suited to unit tests.

In C++0x, there is Yet Another Way to initialize objects, using the {} syntax (uniform initialization). Except, it isn’t really new, it’s the way we’ve always been initializing arrays and structs:

int i[] = {1,4,9};

The difference is we can now use this syntax to initialize any object. Why does this matter, and what is wrong with the old constructor syntax? Nothing is wrong with it in fact, and it will still see a lot of use. But in some cases, {} leads to simpler and easier code.

One area in particular is initialization of containers. It was always a bit of a hassle to initialize for instance a vector, where you would need to do something like this:

    string a[] = {"foo", "bar"};
    vector<string> v(a, a+2);

In C++0x however, you can do

    vector<string> v = {"foo", "bar"};

One area where I find myself initializing containers manually a lot is in unit tests. Here is an example:

#include <gtest/gtest.h>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int count_sheep(const vector<string>& animals) {
    return count(animals.begin(), animals.end(), "sheep");
}

TEST(TestCountSheep, returns_zero_when_there_are_no_sheep) {
    ASSERT_EQ(0, count_sheep({"pig", "cow", "giraffe"})); //here
}

TEST(TestCountSheep, returns_all_sheep) {
    ASSERT_EQ(2, count_sheep({"sheep", "cow", "sheep"})); //and here
}

Note the calls to count_sheep(), where the container is declared and initialized in line, without any mention of string, vector or temporary arrays!

To compile this, you need g++ 4.5 (sudo apt-get install g++-4.5). It might also be possible in Visual Studio 2010 if you are so inclined. This example also uses the Google C++ Testing Framework (sudo apt-get install libgtest-dev). Here is the full command to compile and run the example:

g++-4.5 * -std=c++0x -lgtest_main -lgtest -lpthread && ./a.out

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter