How does boost::phoenix work?

The boost::phoenix is simply mysterious – it doesn’t look like C++.

for_each(v.begin(), v.end(),
    if_(arg1 > 5)
    [
        cout << arg1 << ", "
    ]
);

It’s the standard std::for_each. The third parameter is usually a functor but boost::phoenix lets you inline the code there.

To understand the mystery, I try to write simple boost::phoenix from the scratch.

The basic idea is to build a function object with the syntax similar to C++. For example, let’s take the simplest std::count_if example. Here, std::count_if takes a functor which always returns true. The functor is defined as true_.

struct bool_{
  bool_(bool v) : v(v){}
  bool operator()(int&) { return v;}
  bool v;
};
static const bool_ true_(true);
static const bool_ false_(false);

void main(){
    int a[] = {1, 2, 3, 4, 5};
    int* beg = a;
    int* end = a + sizeof(a) / sizeof(a[0]);
    ptrdiff_t ret = count_if(beg, end, true_);
    cout << ret << endl;
}

 

It’s not useful until we support operators. How to write an expression to return true when the argument value is odd number? In other words, how to write the expression like this?

count_if(beg, end, _1 % 2);

The functor _1 should return the value of the argument.

struct argument_t {
    int operator()(int& v){
        return v;
    }
};
static const argument_t _1;

 

The next step is to define the % operator which takes an argument_t object as left hand side value and integer as right hand side value. The operator returns a functor which perform modulo operation when the argument is supplied.

struct mod_composite {
    argument_t numerator;
    int denominator;
    
    mod_composite(argument_t a, int b)
        :numerator(a),
         denominator(b)
    {
    }

    int operator()(int& v){
        return numerator(v) % denominator;
    }
};

mod_composite operator%(argument_t a, int b) {
    return mod_composite(a, b);
}

 

The whole program is below:

#include <iostream>
#include <algorithm>

using namespace std;

struct argument_t {
    int operator()(int& v){
        return v;
    }
};

struct mod_composite {
    argument_t numerator;
    int denominator;
    
    mod_composite(argument_t a, int b)
        :numerator(a),
         denominator(b)
    {
    }

    int operator()(int& v){
        return numerator(v) % denominator;
    }
};

mod_composite operator%(argument_t a, int b) {
    return mod_composite(a, b);
}

static const argument_t _1;

void main(){
    int a[] = {1, 2, 3, 4, 5};
    int* beg = a;
    int* end = a + sizeof(a) / sizeof(a[0]);
    ptrdiff_t ret = count_if(beg, end, _1 % 2);

    cout << ret << endl;
}

 

The solution has a problem. The mod_composite::operator function is hard-coded to pass the argument to the numerator functor. How to write, for example, return true when the argument is a factor of 12?

    ptrdiff_t ret = count_if(beg, end, 12 % _1);

 

The first step is to write a dedicated type for all the terms of the expression. The object accepts argument and handle it differently depending on the internal type.

template< typename T>
struct term_t {
    term_t(){}
    term_t(T v) : v(v){}
    int operator()(int& arg);
    T v;
};
template<typename T>
int term_t<T>::operator()(int& arg){
    return v(arg);
}
template<>
int term_t<int>::operator()(int& arg){
    return v;
}

 

The operator is now takes term_t object instead of the internal types.

template <typename LT, typename RT>
mod_composite<LT, RT> operator%(term_t<LT> a, term_t<RT> b) {
    return mod_composite<LT, RT>(a, b);
}

 

As a result, the mod_composite object can unconditionally apply the argument on both terms.

template< typename LT, typename RT>
struct mod_composite {
    term_t<LT> numerator;
    term_t<RT> denominator;
    
    mod_composite(term_t<LT> a, term_t<RT> b)
        :numerator(a),
         denominator(b)
    {
    }

    int operator()(int& v){
        return numerator(v) % denominator(v);
    }
};

 

Here is the whole source code.

#include <iostream>
#include <algorithm>

using namespace std;

template< typename T>
struct term_t {
    term_t(){}
    term_t(T v) : v(v){}
    int operator()(int& arg);
    T v;
};
template<typename T>
int term_t<T>::operator()(int& arg){
    return v(arg);
}
template<>
int term_t<int>::operator()(int& arg){
    return v;
}

struct argument_t {
    int operator()(int& v){
        return v;
    }
};

template< typename LT, typename RT>
struct mod_composite {
    term_t<LT> numerator;
    term_t<RT> denominator;
    
    mod_composite(term_t<LT> a, term_t<RT> b)
        :numerator(a),
         denominator(b)
    {
    }

    int operator()(int& v){
        return numerator(v) % denominator(v);
    }
};

template <typename LT, typename RT>
mod_composite<LT, RT> operator%(term_t<LT> a, term_t<RT> b) {
    return mod_composite<LT, RT>(a, b);
}

static const term_t<argument_t> _1;

template <typename T>
term_t<T> val_(T v) {
    return term_t<T>(v);
}

void main(){
    int a[] = {1, 2, 3, 4, 5};
    int* beg = a;
    int* end = a + sizeof(a) / sizeof(a[0]);
    ptrdiff_t ret = count_if(beg, end, val_(12) % _1);

    cout << ret << endl;
}

 

Advertisements

About Moto

Engineer who likes coding
This entry was posted in C++. 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