blob: 661965764fb2f465c7b0d35cc7b143542fece0a0 [file] [log] [blame]
//[ Calc3
// Copyright 2008 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// This example enhances the arithmetic expression evaluator
// in calc2.cpp by using a proto transform to calculate the
// number of arguments an expression requires and using a
// compile-time assert to guarantee that the right number of
// arguments are actually specified.
#include <iostream>
#include <boost/mpl/int.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/min_max.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/context.hpp>
#include <boost/proto/transform.hpp>
namespace mpl = boost::mpl;
namespace proto = boost::proto;
using proto::_;
// Will be used to define the placeholders _1 and _2
template<typename I> struct placeholder : I {};
// This grammar basically says that a calculator expression is one of:
// - A placeholder terminal
// - Some other terminal
// - Some non-terminal whose children are calculator expressions
// In addition, it has transforms that say how to calculate the
// expression arity for each of the three cases.
struct CalculatorGrammar
: proto::or_<
// placeholders have a non-zero arity ...
proto::when< proto::terminal< placeholder<_> >, proto::_value >
// Any other terminals have arity 0 ...
, proto::when< proto::terminal<_>, mpl::int_<0>() >
// For any non-terminals, find the arity of the children and
// take the maximum. This is recursive.
, proto::when< proto::nary_expr<_, proto::vararg<_> >
, proto::fold<_, mpl::int_<0>(), mpl::max<CalculatorGrammar, proto::_state>() > >
>
{};
// Simple wrapper for calculating a calculator expression's arity.
// It specifies mpl::int_<0> as the initial state. The data, which
// is not used, is mpl::void_.
template<typename Expr>
struct calculator_arity
: boost::result_of<CalculatorGrammar(Expr, mpl::int_<0>, mpl::void_)>
{};
// For expressions in the calculator domain, operator ()
// will be special; it will evaluate the expression.
struct calculator_domain;
// Define a calculator context, for evaluating arithmetic expressions
// (This is as before, in calc1.cpp and calc2.cpp)
struct calculator_context
: proto::callable_context< calculator_context const >
{
// The values bound to the placeholders
double d[2];
// The result of evaluating arithmetic expressions
typedef double result_type;
explicit calculator_context(double d1 = 0., double d2 = 0.)
{
d[0] = d1;
d[1] = d2;
}
// Handle the evaluation of the placeholder terminals
template<typename I>
double operator ()(proto::tag::terminal, placeholder<I>) const
{
return d[ I() - 1 ];
}
};
// Wrap all calculator expressions in this type, which defines
// operator () to evaluate the expression.
template<typename Expr>
struct calculator_expression
: proto::extends<Expr, calculator_expression<Expr>, calculator_domain>
{
typedef
proto::extends<Expr, calculator_expression<Expr>, calculator_domain>
base_type;
explicit calculator_expression(Expr const &expr = Expr())
: base_type(expr)
{}
BOOST_PROTO_EXTENDS_USING_ASSIGN(calculator_expression)
// Override operator () to evaluate the expression
double operator ()() const
{
// Assert that the expression has arity 0
BOOST_MPL_ASSERT_RELATION(0, ==, calculator_arity<Expr>::type::value);
calculator_context const ctx;
return proto::eval(*this, ctx);
}
double operator ()(double d1) const
{
// Assert that the expression has arity 1
BOOST_MPL_ASSERT_RELATION(1, ==, calculator_arity<Expr>::type::value);
calculator_context const ctx(d1);
return proto::eval(*this, ctx);
}
double operator ()(double d1, double d2) const
{
// Assert that the expression has arity 2
BOOST_MPL_ASSERT_RELATION(2, ==, calculator_arity<Expr>::type::value);
calculator_context const ctx(d1, d2);
return proto::eval(*this, ctx);
}
};
// Tell proto how to generate expressions in the calculator_domain
struct calculator_domain
: proto::domain<proto::generator<calculator_expression> >
{};
// Define some placeholders (notice they're wrapped in calculator_expression<>)
calculator_expression<proto::terminal< placeholder< mpl::int_<1> > >::type> const _1;
calculator_expression<proto::terminal< placeholder< mpl::int_<2> > >::type> const _2;
// Now, our arithmetic expressions are immediately executable function objects:
int main()
{
// Displays "5"
std::cout << (_1 + 2.0)( 3.0 ) << std::endl;
// Displays "6"
std::cout << ( _1 * _2 )( 3.0, 2.0 ) << std::endl;
// Displays "0.5"
std::cout << ( (_1 - _2) / _2 )( 3.0, 2.0 ) << std::endl;
// This won't compile because the arity of the
// expression doesn't match the number of arguments
// ( (_1 - _2) / _2 )( 3.0 );
return 0;
}
//]