Writing Code Inside Out Using Lambdas


I’ve read in a few places now that the C++ of today isn’t the C++ of yesterday, and I agree with that. It’s come so far. Strangely, I find myself writing sections of my C++ code more like I write Javascript. Before you tar and feather me, let me explain…

C++’s later specifications—C++11, C++14, etc.—include tried and true functionality from other languages, like lambdas. Lambdas are basically what Javascripters would call anonymous functions. I love lambdas! They allow me to solve code duplication issues that I otherwise wouldn’t be able to solve without additional maintenance overhead.

Check this out; we all know about loops. Loops allow us to write code once that runs over and over again, like so…

bool DoStuff()
{
    // do
    // the
    // same
    // things
    // here
    return true;
}

int main()
{
    int x[5]={1,2,3,4,5};

    StartSomeThings();
    for (const int y : x) DoStuff();

    SnapShot();
    for (const int y : x) DoStuff();

    Restore();
    for (const int y : x) DoStuff();
}

DoStuff() does the same thing 15 times, but we only had to write what it does once. I love loops! But, they have a limited use case, that being different-setup, same-action. We set up everything the loop needs however we need to do it, and then run the loop and it does the same action on the context that we’ve set up for it. But what if I need same-setup, different-action? Frankly, then things become a mess.

bool DoStuff()
{
    // setup
    int x[5]={1,2,3,4,5};
    for (const int y : x)
    {
        // do
        // something
        // here
        return true;
    }
    // teardown
}

bool DoSlightlyDifferent()
{
    // same setup
    int x[5]={1,2,3,4,5};
    for (const int y : x)
    {
        // do
        // something
        // slightly
        // different
    }
    return true;
    // same teardown
}

bool DoVeryDifferent()
{
    // same setup (again...)
    int x[5]={1,2,3,4,5};
    for (const int y : x)
    {
        // do
        // something
        // very
        // different
    }
    return true;
    // same teardown
}

int main()
{
    DoStuff();
    DoSlightlyDifferent();
    DoVeryDifferent();
}

I have to move the loop into a function, then create a different function for each possible loop body. Each function has the exact same setup, and let’s assume main() is not supposed to know or care what the setup is. Now, when I change DoStuff() in the future, I have to remember to change the exact same things for DoSlightlyDifferent() and DoVeryDifferent(). This is error prone and a maintenance nightmare for organization that don’t have good documentation (which is, like, every organization). Sure, in this generic example, I could just set up the context and pass the result into the function, but this could potentially will result in a function declaration that has a ridiculous number of parameters. Nobody wants to look at that.

Lambdas to the Rescue

Now let’s create an action that accepts a function pointer, and use a lambda so we don’t have to define additional class members. (I didn’t feel like writing out a whole class definition so pretend this process exists inside a class).

#include <functional>

bool DoStuff(std::function<void()> action)
{
    // setup, same setup, and same setup (again...)
    int x[5]={1,2,3,4,5};
    for (const int y : x)
    {
        action(); // different do stuffs
        return true;
    }
    // same teardown
}

int main()
{
    DoStuff([]() { /* do something */ });
    DoStuff([]() { /* do something slightly different */ });
    DoStuff([]() { /* do something very different */ });
}

Wow! Look how much smaller that code got, even in this stupid example! Now I’m once again only changing code in a single place without expanding the number of declared functions just in the name of tidiness. I used this extensively in my networking code. Think same setup (network), but different action (GET, POST, DELETE). I find myself doing this more and more in my code and I’ve taken to calling it writing code inside-out, coming at writing functions from the complete opposite angle to the traditional one.

Lambdas, you are fantastic!


Leave a Reply