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
Egg is a header-only library. There is no need to build or link to any lib files.
Egg requires Boost C++ Libraries Version 1.34.1 or later. You don't need to build any Boost libraries.
Egg is known to work on the following platforms:
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) { 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; } } } // namespace imperfect
Take by reference, for a Range isn't always Copy Constructible. |
|
Prints |
Even if you don't fully understand this code snippet, you can notice that it reveals several problems:
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.
make_filtered
is
called without qualification, your compiler will look into all the
associated namespaces in order to find the name make_filtered
.
make_filtered
clearly
contains cut-n-paste tokens both in function declaration and body.
This is well-known anti-pattern.
make_filtered
?
It is not good to document complicated return types.
Egg solves these problems.
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(); 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_< T_make_filtered(std::string &, bool(*)(char)) >::type lowers = make_filtered(src, &is_lower); foreach (char ch, lowers) { std::cout << ch; } foreach (char ch, make_filtered(make_filtered(src, &is_not_X), &is_not_Y)) { std::cout << ch; } }
This macro ensures static initialization. |
|
|
|
Prints |
|
Notice
outmost |
|
Prints |
Egg solves the problems stated above:
make_filtered
is a Function Object.
make_filtered
without extra efforts.
make_filtered
is
an object, so that ADL isn't triggered.
iter_t
and result_type
from
operator()
body.
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.
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; void quick_start_pipable() { std::string src("abXcYdXefXgYhY"); foreach (char ch, src|filtered(&is_not_X)|filtered(&is_not_Y)) { std::cout << ch; } }
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); ::my_foreach(make_Filtered(make_Filtered(bll::_1, &is_not_X), &is_not_Y)); }
Using |
Function Adaptors can provide several "views" into functions on demand. Let's look into some usefull adaptors.
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< 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;
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> > { 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); }
|
|
Prints |
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; }
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); 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); std::cout << egg::curry2(plus)(i6)(i7); // Lv: 0 1 // \x -> negate(x) std::cout << egg::nest1(negate)(_0_(_1))(i7); std::cout << egg::lazy(negate)(_1)(i7); }
Note that the three adaptors, unlike nestN
, can support static
initialization.
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)); std::cout << egg::unfuse(egg::fuse(plus))(1, 2); }
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); std::cout << good_plus(1, 2); std::cout << good_plus(1, 2, 3); }
Variadic functions
can be written by unfuse
in one shot. See also variadic
,
which is short-cut to unfuse
.
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")); #endif std::cout << int_plus(1, 2); }
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.
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.
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.
The Forwarding Strategies
section defines strategy tags for argument forwarding. Almost all of Egg
requires these as parameters. (You will see
token is used everywhere.)
Fortunately, the default behavior, __Stg
by_perfect
, works so well in most
cases that you can skip reading Forwarding
Strategies when first reading the documentation.
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 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".
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 is a set of workarounds.
For now, only BOOST_EGG_CONST
might be worth reading.