Tuesday 17 May 2011

Fluent Functors

I've been learning about BOOST Spirit; a C++ expression based compiler generator.  One of the examples is for a Roman Numeral parser.  This contained the following interesting code for pre-loading a symbol table.

struct ones_ : qi::symbols<char, unsigned>
{
    ones_()
    {
        add
            ("I"    , 1)
            ("II"   , 2)
            ("III"  , 3)
            ("IV"   , 4)
            ("V"    , 5)
            ("VI"   , 6)
            ("VII"  , 7)
            ("VIII" , 8)
            ("IX"   , 9)
        ;
    }

} ones;


So,
  • struct ones_ is a new class definition.
  • ones_() is the constructor
  • The call to add("I", 1) is a call to the member function add associating the string "I" with the value 1 by adding them to the symbol table.
There's nothing particularly strange there.  However, the continuation, i.e. add()()()()()... looked a little odd and puzzled me for a minute or so.  Then I realized add() must be returning *this and the other ()s were invoking the  parenthesis operator, i.e. in this case ones_& operator()(const char*, const int);

Following this little revelation to confirm the theory I constructed the following program which sums the numbers 1-6 printing 21.
#include <iostream>

class Func
{
private:
    int m_sum;

public:
    Func(const int n) : m_sum(n) { /* Empty */ }

    const int Sum() const { return m_sum; }

    Func& operator()(const int n)
    {
 m_sum += n;

 return *this;
    }

    static Func add(int n) { return Func(n); }
};

int main()
{
    std::cout << Func::add(1)(2)(3)(4)(5)(6).Sum() << std::endl;
}

All that's needed is a seed function, in this case add() which returns *this followed by operator()().  It would work fine without the named seed function using just operator()() but then it would loose a little meaning.

This isn't really that helpful and in most circumstances probably constitutes obfuscated code.  However it's certainly cute and where overloading is done fully and with meaning of which Spirit is a case in point then it becomes an effective and usable syntax.

As Fluent Programming seems to be on rise this another demonstration of it within C++, just like iostreams.  The added Syntactical Sugar provided by C++'s Functor mechanism makes for Fluent Functors.

No comments: