Click here to Skip to main content
15,884,177 members
Articles / Programming Languages / C++
Article

Expression-based callbacks

Rate me:
Please Sign up or sign in to vote.
4.60/5 (7 votes)
12 Mar 20062 min read 50.6K   17   18
An easy way to provide expression-based callbacks in STL containers.

Introduction

In most cases where collections are used, the need to iterate over a collection, do a computation, and collect the results in a variable, arises often. This article shows an easy way to do this in C++, different from the most common approach, using containers together with expression templates. The article will use STL, but the technique can be applied to any container.

The common approach

The most usual approach to iteration over a collection is to use a for-loop. Writing for loops becomes tedious after a while, therefore many languages offer a 'for-each' statement that allows iteration over a collection. While C++ does not offer such a statement, it is very easy to implement it in various ways. The most common approach is to make a for-loop in a function that accepts a callback, like this:

template <class C> void forEach(C &c, 
            void (*callback)(C::value_type)) {
    for(C::iterator it = c.begin(); 
                    it != c.end(); ++it) {
        callback(*it);
    }
}

But this approach has some disadvantages; it interrupts the thought process of the programmer by making him/her think about where to place the callback, and also how to access the local data from inside the callback.

Expression templates

Expression templates come as a rescue: by using operator overloading, classes that represent an expression bound to local arguments can be used as a callback to a for-each loop. Before explaining how to code such classes, let's see an example of usage:

#include <list>
#include <iostream>
using namespace std;

int main()
{
    list<int> l;
    
    l.push_back(1);
    l.push_back(2);
    l.push_back(3);
    
    int i = 0;
    Expr< int > x;
    forEach(l, i, x += i);
    cout << x;
    getchar();
    return 0;
}

As you can see, it is much easier to sum a list of numbers using the above code!

How it works

Here is the important line of code; let's focus on it:

forEach(l, i, x += i);

C++ does not have lambda functions or closures, but it has operator overloading. So in the above code, x is an object that when operator += is applied to it, it does not actually add anything, but it creates a function object that encapsulates the variables x and i, and when invoked, it will perform the action.

The class Expr that x is an instance of is:

template <class T> class Expr {
public:
    Expr() : m_v(T()) {
    }

    ExprAddAssign<T> operator += (T &i) {
        return ExprAddAssign<T>(m_v, i);
    }
    
    operator T () const {
        return m_v;
    }
    
private:
    T m_v;    
};

We can see that it encapsulates a value of type T. We also see that operator += returns an instance of class ExprAddAssign, which is this class:

template <class T> class ExprAddAssign {
public:
    ExprAddAssign(T &lv, const T &rv) : 
                  m_lv(lv), m_rv(rv) {
    }
    
    void operator ()() const {
        m_lv += m_rv;
    }

private:
    T &m_lv;
    const T &m_rv;
};

The above class contains two bindings: one for the rvalue and one for the lvalue. The lvalue is assigned the value of rvalue. The code for the function 'forEach' becomes:

template <class C, class T, class F> 
         void forEach(const C &c, T &v, F &obj) {
    for(C::const_iterator it = c.begin(); 
                          it != c.end(); ++it) {
        v = *it;
        obj();
    }
}

Conclusion

A wide variety of expression templates can be programmed. Even constructs like if-then-else can be coded using a Smalltalk like syntax. Example:

forEach(l, (i > 2).ifTrue(x += i));

I actually accidentally came up with this solution, searching for a way to make lambda functions in C++. I realized that one does not need lambda functions, as long as lambda parameters can be declared as local variables.

I am surprised that the STL library does not contain such a set of classes. It would offer great power to C++...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Greece Greece
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralBoost Pin
Stephen Hewitt12-Mar-06 20:41
Stephen Hewitt12-Mar-06 20:41 
GeneralRe: Boost Pin
Stuart Dootson12-Mar-06 21:03
professionalStuart Dootson12-Mar-06 21:03 
GeneralRe: Boost Pin
Stephen Hewitt12-Mar-06 21:44
Stephen Hewitt12-Mar-06 21:44 
GeneralRe: Boost Pin
Achilleas Margaritis13-Mar-06 0:07
Achilleas Margaritis13-Mar-06 0:07 
GeneralRe: Boost Pin
Stephen Hewitt13-Mar-06 0:29
Stephen Hewitt13-Mar-06 0:29 
GeneralRe: Boost Pin
Achilleas Margaritis13-Mar-06 4:25
Achilleas Margaritis13-Mar-06 4:25 
GeneralRe: Boost Pin
Stephen Hewitt13-Mar-06 11:31
Stephen Hewitt13-Mar-06 11:31 
GeneralRe: Boost Pin
Achilleas Margaritis13-Mar-06 11:51
Achilleas Margaritis13-Mar-06 11:51 
GeneralRe: Boost Pin
Stephen Hewitt13-Mar-06 11:55
Stephen Hewitt13-Mar-06 11:55 
GeneralRe: Boost Pin
Achilleas Margaritis14-Mar-06 0:09
Achilleas Margaritis14-Mar-06 0:09 
GeneralRe: Boost Pin
Ivan Kolev14-Mar-06 20:53
Ivan Kolev14-Mar-06 20:53 
GeneralRe: Boost Pin
Stephen Hewitt14-Mar-06 22:26
Stephen Hewitt14-Mar-06 22:26 
GeneralRe: Boost Pin
Jerry Jeremiah20-Jul-06 12:37
Jerry Jeremiah20-Jul-06 12:37 
GeneralRe: Boost Pin
Stephen Hewitt20-Jul-06 14:13
Stephen Hewitt20-Jul-06 14:13 
GeneralRe: Boost Pin
Jerry Jeremiah20-Jul-06 19:22
Jerry Jeremiah20-Jul-06 19:22 
GeneralRe: Boost Pin
Stephen Hewitt20-Jul-06 19:52
Stephen Hewitt20-Jul-06 19:52 
GeneralRe: Boost Pin
Jerry Jeremiah23-Aug-09 12:57
Jerry Jeremiah23-Aug-09 12:57 
GeneralDLinq Pin
Keith Farmer12-Mar-06 15:18
Keith Farmer12-Mar-06 15:18 
There are some fellow C++ users (C++/CLI, actually) who would be delighted to see how this could be fully fleshed out to support the expression trees in DLinq.

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=294311&SiteID=1[^]

-----
Keith J. Farmer [MSFT:VC#/DLinq]

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.