C++11, fun(ctional) times with lambdas

edit : Updated Event class code to use std::pair instead of a private struct

This is a long blog post and quite technical in nature.

Besides lambdas, many other C++11-only things will appear in the code examples.

C++11 gained a new feature called lambda, which can be used to conveniently express things that previously (C++98/03) required using function pointers or function objects. The canonical example would be calling std::sort with a custom comparator.

The C++98/03 way, using a comparison function :

bool my_compare(const std::string& a, const std::string& b)
{
    return to_lower(a)<to_lower(b);
}

void test_function_pointer_sort()
{
    std::vector<std::string> mydata{"dog", "cat", "monkey", "beetle", "kangaroo", "elephant"};
    std::sort(mydata.begin(),mydata.end(),my_compare);
    for (auto e : mydata)
        std::cout << e << " ";
}

While the above code works, the additional function is annoying to write and the compiler probably won’t be able to inline the call to the comparator, which is bad for performance.

However, the function object approach often would allow the inlining to happen (C++98/03) :

struct my_comparator
{
    bool operator()(const std::string& a, const std::string& b)
    {
        return to_lower(a)<to_lower(b);
    }
};

void test_function_object_sort()
{
    std::vector<std::string> mydata{"dog", "cat", "monkey", "beetle", "kangaroo", "elephant"};
    std::sort(mydata.begin(),mydata.end(),my_comparator());
    for (auto e : mydata)
        std::cout << e << " ";
}

The high probability of inlining is the only good part in that, however. The class adds even worse non-local noise into the code than the standalone comparator function. Yes, that is a separate class (even though declared as a struct here) we have to write to provide the std::sort function a custom way to sort!

Fortunately, C++11 lambdas come to the rescue. They essentially just automatically generate a hidden class like in the above code example, and allow
the “meat” of the needed class to be written locally inside our function.

void test_lambda_sort()
{
    std::vector<std::string> mydata{"dog", "cat", "monkey", "beetle", "kangaroo", "elephant"};
    // 3rd argument to the sort function is now a lambda expression
    std::sort(mydata.begin(),mydata.end(),
              [](const std::string& a, const std::string& b) { return to_lower(a)<to_lower(b); } );
    for (auto e : mydata)
        std::cout << e << " ";
}

The syntax of the lambdas may feel a bit odd at first (“what the heck is [] there in the middle?”), but once one gets the hang of using them, there’s no turning back. The example with the custom sorting comparator is just the tip of the iceberg, really. Lambdas can be utilized almost anywhere where there is a need to use code that needs to be actually run from elsewhere, perhaps at some later time. Examples would be running code in other threads or code that needs to run when GUI widgets/controls have been manipulated.

For example, in GUI code the stuff that needs to happen in response to widget manipulation is often trivial and it is a royal annoyance to have to write new functions to implement them.

Pseudo-Qt4 code style code :

MainWindow::MainWindow(...)
{
  ...some other stuff initialized here...
  m_slider=new QSlider(this);
  connect(m_slider,SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int));
  ---
}

void MainWindow::onValueChanged(int value)
{
  m_audioThing->setParameter(0,1.0/100*value);
}

Assuming we had a GUI framework that could deal with lambdas (Qt5 actually can, but I haven’t tried that yet), we could write something like :

MainWindow::MainWindow(...)
{
  ...some other stuff initialized here...
  m_slider=new XSlider(this);
  // lambda "captures" our current MainWindow object with [this], so it can use the member m_audioThing
  m_slider->ValueChanged=[this](int value) { m_audioThing->setParameter(0,1.0/100*value); };
}

Our XSlider could have a member called ValueChanged that can be assigned a callable object like a lambda. The C++11 Standard Library offers a great facility for this, the std::function class template.

The XSlider class declaration could be something like (omitting all non-relevant stuff for this example) :

class XSlider : public XWidget
{
public:
  std::function<void(int)> ValueChanged; // should probably be encapsulated, but public variable will do fine here...
  void onMouseDrag(XMouseEvent& ev) // implements a virtual function
  {
    // check there's an assigned callable, std::function converts to bool here
    if (ValueChanged)
    { 
      // make the call, which actually causes the lambda back in the MainWindow or somewhere else to run
      ValueChanged(convert_mouse_y_pos_to_value(ev.y())); 
    }
  }
}

We would probably want to have the ability to have multiple callback sites for the XSlider event. Instead of using std::function directly, we could write a class that can contain multiple std::functions. To make things a bit more generic, we make this templated so that the events can deliver values of any suitable type.

template<typename T>
class Event
{
private:
    using listener_entry=std::pair<std::function<void(T)>, uint32_t>;
    std::vector<listener_entry> m_listeners;
public:
    uint32_t add_listener(std::function<void(T)> f)
    {
        static uint32_t id=0;
        id++;
        m_listeners.push_back({f,id});
        return id;
    }
    void remove_listener(uint32_t id)
    {
        for (int i=0;i<m_listeners.size();i++)
            if (m_listeners[i].second==id)
                m_listeners.erase(m_listeners.begin()+i);
    }
    void run(T x)
    {
        for (auto& e : m_listeners)
            e.first(x);
    }
};

Our slider class could then look like :

class XSlider : public XWidget
{
public:
  Event<int> ValueChanged; // should probably be encapsulated, but public variable will do fine here...
  void onMouseDrag(XMouseEvent& ev) // implements a virtual function
  {
      // Causes the lambda(s) back in the MainWindow or somewhere else to run
      // No longer need to check for ValueChanged validity, as an Event with no callback functions added
      // does nothing when run() is called
      ValueChanged.run(convert_mouse_y_pos_to_value(ev.y())); 
    }
  }
}

MainWindow code could look like :

MainWindow::MainWindow(...)
{
  ...some other stuff initialized here...
  m_slider=new XSlider(this);
  // lambda "captures" our current MainWindow object with [this], so it can use the member m_audioThing
  m_slider->ValueChanged.add_listener([this](int value) 
  { m_audioThing->setParameter(0,1.0/100*value); });
}

More involved example with multiple sliders, first with Qt4 style code (using QObject::property()/setProperty() and sender() is not necessarily good style but at least using those makes this about as short as it can be done in Qt4) :

MainWindow::MainWindow(...)
{
    ... init other stuff here...
    for (int i=0;i<8;i++)
    {
        QSlider* slid=new QSlider(this);
        slid->setProperty("par_index",i);
        connect(slid,SIGNAL(valueChanged(int)),this,SLOT(onSliderMoved(int)));
    }
}   
    
void MainWindow::onSliderMoved(int value)
{
        // ensure that if this function is called directly (by accident probably), nothing happens as we require the sender() to be valid
        if (sender()==nullptr) 
            return;
        int par_index=sender()->property("par_index").toInt();
        m_audioThing->setParameter(par_index,1.0/100*value));
}

C++11/lambda based approach in our hypothetical GUI framework :

MainWindow::MainWindow(...)
{
  ...other stuff inited here...
    for (int i=0;i<8;i++)
    {
        XSlider* slid=new XSlider(this);
        slid->ValueChanged.add_listener([i,this](int x)
        {
            m_audioThing->setParameter(i,1.0/100*x);
        });
    }
}

There is no longer the need to fool around with variant based properties (those can be handy for other purposes though), string based lookups of those properties or ensure the “on slider moved” callback doesn’t crash, in addition to not having to write the separate function. The lambda captures the needed “i” index variable directly by copying it.

The presented Event class has the downside it doesn’t deal with cross-thread communication, which the Qt signals and slots system does in a fairly transparent manner. I am fairly confident a similar system to the Qt one could however be built around a class like Event.

Then to another thing, which prompted the title of this blog post. As lambdas can be used to “move” code to be executed elsewhere, it is possible to write a function as follows, for measuring how long a piece of code runs (for Windows, Unix etc could use clock_gettime etc instead of QueryPerformance* :

template<typename F>
double time_call(F f)
{
    LARGE_INTEGER freq, start_time, end_time;
    QueryPerformanceFrequency(&freq);
    QueryPerformanceCounter(&start_time);
    f();
    QueryPerformanceCounter(&end_time);
    return double( end_time.QuadPart - start_time.QuadPart ) / freq.QuadPart * 1000.0;
}

This function itself doesn’t use a lambda, it just assumes it is passed a thing it can call with f() and lambdas are compatible. Client code :

void test_time_call()
{
    int amount_to_sleep=1000;
    double elapsed=time_call([=]()
    {
      Sleep(amount_to_sleep);
    });
    std::cout << elapsed << " ms\n";
    std::vector<double> data(100000000);
    elapsed=time_call([&]()
    {
       std::mt19937 gen; std::uniform_real_distribution<double> dist(-0.5,0.5);
       for (int i=0;i<100000000)
         data[i]=dist(gen);
    });
    std::cout << elapsed << " ms\n";
}

Contrast this with a more traditional approach that involves using a class (which isn’t given here, but the implementation is fairly obvious) :

void test_elapsed_timer()
{
  ElapsedTimer etimer; // constructor kicks off the counting, thanks for that, at least
  int amount_to_sleep(1000);
  Sleep(amount_to_sleep);
  std::cout << etimer.elapsed() << " ms\n";
  etimer.restart();
  std::mt19937 gen; std::uniform_real_distribution<double> dist(-0.5,0.5);
  std::vector<double> data(100000000);
  for (int i=0;i<100000000)
    data[i]=dist(gen);
  std::cout << etimer.elapsed() << " ms\n";
}

As can be seen, the lambda approach doesn’t necessarily in this case reduce the amount of code lines (which could depend a bit on the used code formatting, though) but it does remove the need to involve the elapsed timer object and we don’t need to remember to restart the timer between multiple uses of the object. Both the object and the lambda based approaches have their pros and cons here. Similar patterns that do more intricate things than just time code sections can be designed. For example, any object can be wrapped inside another class to ensure operations on the object happen within some desired conditions, such as mutex locks. I will leave implementing such a wrapper class as an exercise for the reader for now. I might write about that later in another blog post.

I didn’t show how C++11 facilities, among them lambdas, can make it easier to run work in separate threads. That deserves a whole another blog post.

Thanks for reading and apologies for any typos, inaccuracies, omissions and obscurities. Many will no doubt have crept in, as this must have been the longest blog post I’ve done so far.

Advertisements
This entry was posted in C++, Programming. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s