Boost C++ Libraries

PrevUpHomeNext

Quick Start

Installing Egg
Using Egg
Reading Doc
Getting Egg

You can get egg.zip from Boost.Vault/FunctionObjects. Also, if you have an svn client, the Egg svn repository is available using the following command:

svn co https://p-stade.svn.sourceforge.net/svnroot/p-stade/trunk/boost/ boost_egg
Building Egg

Egg is a header-only library. There is no need to build or link to any lib files.

Requirements

Egg requires Boost C++ Libraries Version 1.34.1 or later. You don't need to build any Boost libraries.

Portability

Egg is known to work on the following platforms:

  • Microsoft Visual C++ Version 7.1 or later
  • GCC 3.4.4 or later
Problems of function templates

Assume you have to design lazily filtered sequences. (It is called filter in Haskell.) Thanks to Boost.Iterator and Boost.Range, it is pretty straightforward:

#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/result_iterator.hpp>
#define foreach BOOST_FOREACH

namespace imperfect {

    template<class Range, class Predicate>
    boost::iterator_range<
        boost::filter_iterator<
            Predicate,
            typename boost::range_result_iterator<Range>::type
        >
    >
    make_filtered(Range &rng, Predicate pred) 1
    {
        typedef
            boost::filter_iterator<
                Predicate,
                typename boost::range_result_iterator<Range>::type
            >
        iter_t;

        return boost::iterator_range<iter_t>(
            iter_t(pred, boost::begin(rng), boost::end(rng)),
            iter_t(pred, boost::end(rng), boost::end(rng))
        );
    }

    bool is_not_X(char ch) { return ch != 'X'; }

    void quick_start_make_filtered()
    {
        std::string src("abXcdXefXgh");
        foreach (char ch, make_filtered(src, &is_not_X)) {
            std::cout << ch;
        } 2
    }

} // namespace imperfect

1

Take by reference, for a Range isn't always Copy Constructible.

2

Prints abcdefgh.

Even if you don't fully understand this code snippet, you can notice that it reveals several problems:

  1. A function template is not an object.
    • To get a Function Object, you have to make a pointer from a template, which is difficult or sometimes impossible.
  2. Forwarding problem
    • make_filtered above can't take non-const rvalues. So, you have to write also make_filtered(Range const &, Predicate) version. Such irritating job is called the Forwarding Problem.
  3. Unintentional ADL
    • If make_filtered is called without qualification, your compiler will look into all the associated namespaces in order to find the name make_filtered.
  4. Repeated tokens
    • make_filtered clearly contains cut-n-paste tokens both in function declaration and body. This is well-known anti-pattern.
  5. Return type is unknown.
    • How can your clients know the return type of make_filtered? It is not good to document complicated return types.

Egg solves these problems.

Building functions

In short, Egg is a library for building functions. Let's build make_filtered by using one of the Function Builders, poly:

#include <boost/egg/poly.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <locale>

namespace egg = boost::egg;

template<class Range, class Predicate>
struct mono_make_filtered
{
    typedef
        boost::filter_iterator<
            typename boost::remove_cv<Predicate>::type,
            typename boost::range_result_iterator<Range>::type
        >
    iter_t;

    typedef
        boost::iterator_range<iter_t>
    result_type;

    result_type operator()(Range &rng, Predicate &pred) const
    {
        return result_type(
            iter_t(pred, boost::begin(rng), boost::end(rng)),
            iter_t(pred, boost::end(rng), boost::end(rng))
        );
    }
};

typedef
    egg::poly< mono_make_filtered<boost::mpl::_, boost::mpl::_> >::type
T_make_filtered;

T_make_filtered const make_filtered = BOOST_EGG_POLY(); 1


bool is_not_X(char ch) { return ch != 'X'; }
bool is_not_Y(char ch) { return ch != 'Y'; }
bool is_lower(char ch) { return std::islower(ch, std::locale()); }

void quick_start_make_filtered()
{
    std::string src("abXcYdXefXgYhY");

    egg::result_of_< 2
        T_make_filtered(std::string &, bool(*)(char))
    >::type lowers = make_filtered(src, &is_lower);

    foreach (char ch, lowers) {
        std::cout << ch;
    } 3

    foreach (char ch, make_filtered(make_filtered(src, &is_not_X), &is_not_Y)) { 4
        std::cout << ch;
    } 5
}

1

This macro ensures static initialization.

2

boost::result_of isn't used for now. See egg::result_of_.

3

Prints abcdefgh.

4

Notice outmost make_filtered takes rvalue.

5

Prints abcdefgh.

Egg solves the problems stated above:

  1. make_filtered is a Function Object.
    • You can pass it to algorithms as is.
  2. No forwarding problem
    • You can pass non-const rvalues to make_filtered without extra efforts.
  3. No ADL
    • Now make_filtered is an object, so that ADL isn't triggered.
  4. No repeated tokens
    • You can access iter_t and result_type from operator() body.
  5. Boost.ResultOf compatible
    • Your clients can extract return types using standardized interface result_of.

As a bonus, make_filtered can be passed to boost::lambda::bind. In other words, it is a model of Major Function Object.

Higher-order functions

make_filtered(make_filtered(...),...) might be less readable. Egg provides pipable, which is one of the Function Adaptors also known as higher-order functions:

#include <boost/egg/pipable.hpp>

egg::result_of_pipable<T_make_filtered>::type
    const filtered = BOOST_EGG_PIPABLE_L BOOST_EGG_POLY() BOOST_EGG_PIPABLE_R; 1

void quick_start_pipable()
{
    std::string src("abXcYdXefXgYhY");
    foreach (char ch, src|filtered(&is_not_X)|filtered(&is_not_Y)) {
        std::cout << ch;
    }
}

1

Recall the initializer of T_make_filtered is BOOST_EGG_POLY().

pipable can be regarded as Extension method emulation in C++. In addition, lazy, cooperating with Boost.Lambda, adapts a function into lazily evaluated one:

#include <boost/egg/lazy.hpp>
#include <boost/lambda/core.hpp> // _1

template<class MakeRange>
void my_foreach(MakeRange make)
{
    std::string src("abXcYdXefXgYhY");
    foreach (char ch, make(src)) {
        std::cout << ch;
    }
}

void quick_start_lazy()
{
    namespace bll = boost::lambda;

    egg::result_of_<egg::T_lazy(T_make_filtered const &)>::type
        make_Filtered = egg::lazy(make_filtered); 1

    ::my_foreach(make_Filtered(make_Filtered(bll::_1, &is_not_X), &is_not_Y));
}

1

Using lazy as higher-order function.

Function Adaptors can provide several "views" into functions on demand. Let's look into some usefull adaptors.

Getting initializers

As mentioned before, the static initialization is important. What if you don't know a braced-initializer of a Function Object you are adapting? indirect helps:

#include <boost/egg/indirect.hpp>

egg::result_of_pipable< 1
    egg::result_of_indirect<T_make_filtered const *>::type
>::type
    const her_filtered = BOOST_EGG_PIPABLE_L BOOST_EGG_INDIRECT(&make_filtered) BOOST_EGG_PIPABLE_R;

1

Assume you don't know T_make_filtered can be initialized using BOOST_EGG_POLY().

Object generators

It is boring to build Object Generators. generator makes it easy:

#include <boost/egg/generator.hpp>

template<class T>
struct always_result :
    egg::function_facade< always_result<T> > 1
{
    T m_t;
    explicit always_result(T const &t) : m_t(t) { }

    template<class Me, class A>
    struct apply
    {
        typedef T const &type;
    };

    template<class Re, class A>
    Re call(A &) const
    {
        return m_t;
    }
};

egg::generator<
    always_result< egg::deduce<boost::mpl::_1, egg::as_value> >
>::type const always = BOOST_EGG_GENERATOR();

void quick_start_object_generator()
{
    std::cout << always(10)(999); 2
}

1

function_facade might remind you of boost::iterator_facade.

2

Prints 10.

Partial application

You are sure to know boost::lambda::bind in Boost.Lambda. That is an implementation of partial application. But bind expressions may be less readable. nestN offers better syntax so that you can forget bind.

#include <boost/egg/nest.hpp>
#include <boost/lambda/core.hpp> // _1, _2
#include <functional> // minus

using boost::lambda::_1;
using boost::lambda::_2;

// These represent nesting levels.
using egg::_0_;
using egg::_1_;
using egg::_2_;

void quick_start_nest()
{
    int i6 = 6, i7 = 7;
    std::plus<int> plus;
    std::minus<int> minus;

    int r =
    // Lv: 0     1          2
        // \x -> (\(y,z) -> plus(5, y(z, x)))
        egg::nest2(plus)(5, egg::nest2(_1_(_1))(_1_(_2), _0_(_1)))
            (i6)(minus, i7);

    std::cout << r; 1
}

1

Prints 6.

nestN enables you to write any complicated lambda expressions in the automatic manner. In addition, compose, curryN and lazy can be considered as a part of nestN family, for they can be trivially implemented by nestN:

#include <boost/egg/nest.hpp>
#include <boost/egg/compose.hpp>
#include <boost/egg/curry.hpp>
#include <boost/egg/lazy.hpp>
#include <functional> // plus, negate

void quick_start_nest_family()
{
    std::plus<int> plus;
    std::negate<int> negate;

    int i6 = 6, i7 = 7;

// Lv: 0         1
    // \(x,y) -> negate(plus(x, y))
    std::cout << egg::nest1(negate)(egg::nest1(plus)(_0_(_1), _0_(_2)))(i6, i7); 1
    std::cout << egg::compose(negate, plus)(i6, i7);

// Lv: 0     1      2
    // \x -> (\y -> plus(x, y))
    std::cout << egg::nest2(plus)(_0_(_1), _1_(_1))(i6)(i7); 2
    std::cout << egg::curry2(plus)(i6)(i7);

// Lv: 0     1
    // \x -> negate(x)
    std::cout << egg::nest1(negate)(_0_(_1))(i7); 3
    std::cout << egg::lazy(negate)(_1)(i7);
}

1

Prints -13.

2

Prints 13.

3

Prints -7.

Note that the three adaptors, unlike nestN, can support static initialization.

Fusing

Fusing is the technique of transforming a function into one which takes a tuple. A function which takes a tuple is called "fused". In Egg, fusing is implemented by fuse and unfuse:

#include <functional> // plus
#include <boost/tuple/tuple.hpp>
#include <boost/egg/fuse.hpp>
#include <boost/egg/unfuse.hpp>

void quick_start_fusing1()
{
    std::plus<int> plus;

    std::cout << egg::fuse(plus)(boost::make_tuple(1, 2)); 1
    std::cout << egg::unfuse(egg::fuse(plus))(1, 2);
}

1

Prints 3.

If you dislike cut-n-paste and macros, fusing is important:

struct fused_plus
{
    typedef int result_type;

    template<class Tuple>
    int operator()(Tuple const &t) const
    {
        return t.get_head() + (*this)(t.get_tail());
    }

    int operator()(boost::tuples::null_type const &) const
    {
        return 0;
    }
};

typedef egg::result_of_unfuse<fused_plus>::type T_good_plus;
T_good_plus const good_plus = BOOST_EGG_UNFUSE({});

void quick_start_fusing2()
{
    std::cout << good_plus(1); 1
    std::cout << good_plus(1, 2); 2
    std::cout << good_plus(1, 2, 3); 3
}

1

Prints 1.

2

Prints 3.

3

Prints 6.

Variadic functions can be written by unfuse in one shot. See also variadic, which is short-cut to unfuse.

The Return of the Monomorphic

You may complain that Function Adaptors transform a function into templated one. Error messages around templates are actually annoying. mono transforms a function into non-templated one:

#include <boost/egg/mono.hpp>

egg::result_of_mono<T_good_plus, int(int, int)>::type
    const int_plus = BOOST_EGG_MONO_L BOOST_EGG_UNFUSE({}) BOOST_EGG_MONO_R;

void quick_start_mono()
{
#if 0
    std::cout << int_plus(std::string("a"), std::string("b")); 1
#endif
    std::cout << int_plus(1, 2); 2
}

1

Would show an error with clear messages.

2

Prints 3.

Note that a function mono returns is a model of Adaptable Function Object, which can be used with std::bind1st etc.

The organization of Egg is "flat". Egg can be regarded as federation of Function Builders and Function Adaptors. Each component is usually independent from each other, so that you can read the sections with any order.

Notation

The Notation section introduces several keywords for documentation. A word which begins with __ is a keyword. When you forget its semantics, just click the keyword.

Concepts

The Concepts section describes the concepts used in Egg. By the design rationale, Concepts doesn't introduce any new "traits" something like is_bindable<> etc. Those just define the valid expressions and semantics. The most important concepts are Little Function and Major Function Object. Other conepts can read on demand.

Forwarding Strategies

The Forwarding Strategies section defines strategy tags for argument forwarding. Almost all of Egg requires these as parameters. (You will see __Stg token is used everywhere.) Fortunately, the default behavior, by_perfect, works so well in most cases that you can skip reading Forwarding Strategies when first reading the documentation.

Function Builders

Function Builders build Major Function Object type. function is the most primitive builder, which can be stateful. First, it might be preferable to understand function with Little Function concept. Second, I recommend poly, which is a cool builder originally written by David Abrahams.

Function Adaptors

Function Adaptors are higher-order functions which take a function then return adapted function. fuse and unfuse might be one of the most interesting adaptors, which provides "fusing".

Function Objects and Utilities

The Function Objects section provides useful functions which have been requested in Boost users list several times. For Utilities, infix might be interesting, which is ported from FC++.

Workarounds

Workarounds is a set of workarounds. For now, only BOOST_EGG_CONST might be worth reading.


PrevUpHomeNext