Higher-Order Functions with boost::lambda

I’ve been learning Lisp recently (I’ll eventually post something about how and why) but, oddly enough, all this Lisping has got me thinking about C++.

So, without further ado, a higher-order function in C++:


#include <boost/lambda/lambda.hpp>
#include <boost/function.hpp>
#include <iostream>

using namespace std;
using namespace boost::lambda;

template
boost::function foo( T n_ )
{
   // Old code.  Leaks memory.
   //T *n = new T( n_ );
   //return ( *n += _1 );

   boost::shared_ptr pn( new T(n_) );
   return ret( *constant(pn) ) += _1;
}

int main()
{
   // start with one
   boost::function accum = foo(1);
   cout << accum(2) << endl;
   cout << accum(10) << endl;
}

This blows my mind on so many levels, I don’t know where to start. The gist is that foo is a function which generates new functions (ok technically it generates functors, but if you squint, you can’t tell the difference).

Neat.

(Inspired by Paul Graham’s Revenge of the Nerds.)

Update: I originally declared n as a static local, but then I realized what this actually meant (all generated accumulators share n). The current code leaks, but at least it works for multiple accumulator functions. I’m still trying to figure out how to use a smart pointer to plug the leak.

Update: Thanks to Peter Dimov on boost-users, the code above now uses a boost::shared_ptr and is leak-free. For some reason, it won’t build with gcc 4.0.2.

Advertisements

5 Responses to “Higher-Order Functions with boost::lambda”


  1. 1 Bheeshmar November 15, 2005 at 8:14 am

    # In the language of my scarlet mistress (Ruby)
    def foo( initial )
    return lambda { |n| n+=initial }
    end

    accum = foo(1)
    puts “#{accum[2]}”
    puts “#{accum[10]}”

  2. 2 Ryan November 15, 2005 at 1:02 pm

    Mark –

    I don’t have to remind you about what society thinks of guys who lisp do I?

  3. 3 Mark November 15, 2005 at 10:27 pm

    Bheesh – Ruby is bad-ass.
    Ryan – Don’t be thilly, you thexy boy.

  4. 4 Kevin Frei November 17, 2005 at 8:23 am

    You need closures. I’m not quite enough of a template god to get this one right, but this works fine (though the .get()(args) syntax really sucks):

    
    #include <cstdio>
    #include <boost/lambda/lambda.hpp>
    #include <boost/function.hpp>
    #include <boost/shared_ptr.hpp>
    #include <iostream>
    	
    using namespace std;
    using namespace boost;
    using namespace boost::lambda;
    
    
    template <typename T, typename D>
    class closure
    {
        D d;
        T t;
    public:
        closure(const T &t, const D &d) : d(d), t(t) {}
        T &get() { return t; }
    };
    
    template <typename T>
    closure< boost::function<T(T)>,
    boost::shared_ptr<T> > foo( T n_ ) {
        typedef boost::function<T(T)> fn;
        typedef boost::shared_ptr<T>  ptr;
        ptr n( new T(n_) );
        return closure<fn, ptr>(var(*n) += _1, n); }
    
    typedef closure< boost::function<short(short)>,
    boost::shared_ptr<short> > functor_short; typedef
    closure< boost::function<char(char)>,
    boost::shared_ptr<char> > functor_char;
    
    int main()
    {
        // start with one
        {
            functor_short accum = foo((short)1);
            cout << accum.get()(2) << " should be 3"
    << endl;
            cout << accum.get()(10) << " should be 13"
    << endl;
            functor_short accum2 = foo((short)101);
            cout << accum2.get()(2) << " should be
    103" << endl;
            cout << accum2.get()(10) << " should be
    113" << endl;
        }
        functor_char accum = foo((char)3);
        cout << (int)accum.get()(3) << " should be 6"
    << endl;
        cout << (int)accum.get()(13) << " should be
    19" << endl;
    }
    
  5. 5 Bheeshmar November 21, 2005 at 9:37 am

    Very cool, Kevin. I hoped that an implicit conversion operator would get a nicer syntax to work, but I couldn’t figure it out…


Comments are currently closed.




%d bloggers like this: