summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/proto/example/external_transforms.cpp
blob: 3667ebbd69f1d2c93c3813632a05aec6bd0cf313 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//[ CheckedCalc
//  Copyright 2011 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 is an example of how to specify a transform externally so
// that a single grammar can be used to drive multiple differnt
// calculations. In particular, it defines a calculator grammar
// that computes the result of an expression with either checked
// or non-checked division.

#include <iostream>
#include <boost/assert.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/next.hpp>
#include <boost/mpl/min_max.hpp>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/container/generation/make_vector.hpp>
#include <boost/proto/proto.hpp>
namespace mpl = boost::mpl;
namespace proto = boost::proto;
namespace fusion = boost::fusion;

// The argument placeholder type
template<typename I> struct placeholder : I {};

// Give each rule in the grammar a "name". This is so that we
// can easily dispatch on it later.
struct calc_grammar;
struct divides_rule : proto::divides<calc_grammar, calc_grammar> {};

// Use external transforms in calc_gramar
struct calc_grammar
  : proto::or_<
        proto::when<
            proto::terminal<placeholder<proto::_> >
            , proto::functional::at(proto::_state, proto::_value)
        >
      , proto::when<
            proto::terminal<proto::convertible_to<double> >
          , proto::_value
        >
      , proto::when<
            proto::plus<calc_grammar, calc_grammar>
          , proto::_default<calc_grammar>
        >
      , proto::when<
            proto::minus<calc_grammar, calc_grammar>
          , proto::_default<calc_grammar>
        >
      , proto::when<
            proto::multiplies<calc_grammar, calc_grammar>
          , proto::_default<calc_grammar>
        >
        // Note that we don't specify how division nodes are
        // handled here. Proto::external_transform is a placeholder
        // for an actual transform.
      , proto::when<
            divides_rule
          , proto::external_transform
        >
    >
{};

template<typename E> struct calc_expr;
struct calc_domain : proto::domain<proto::generator<calc_expr> > {};

template<typename E>
struct calc_expr
  : proto::extends<E, calc_expr<E>, calc_domain>
{
    calc_expr(E const &e = E()) : calc_expr::proto_extends(e) {}
};

calc_expr<proto::terminal<placeholder<mpl::int_<0> > >::type> _1;
calc_expr<proto::terminal<placeholder<mpl::int_<1> > >::type> _2;

// Use proto::external_transforms to map from named grammar rules to
// transforms.
struct non_checked_division
  : proto::external_transforms<
        proto::when< divides_rule, proto::_default<calc_grammar> >
    >
{};

struct division_by_zero : std::exception {};

struct do_checked_divide
  : proto::callable
{
    typedef int result_type;
    int operator()(int left, int right) const
    {
        if (right == 0) throw division_by_zero();
        return left / right;
    }
};

// Use proto::external_transforms again, this time to map the divides_rule
// to a transforms that performs checked division.
struct checked_division
  : proto::external_transforms<
        proto::when<
            divides_rule
          , do_checked_divide(calc_grammar(proto::_left), calc_grammar(proto::_right))
        >
    >
{};

int main()
{
    non_checked_division non_checked;
    int result2 = calc_grammar()(_1 / _2, fusion::make_vector(6, 2), non_checked);
    BOOST_ASSERT(result2 == 3);

    try
    {
        checked_division checked;
        // This should throw
        int result3 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), checked);
        BOOST_ASSERT(false); // shouldn't get here!
    }
    catch(division_by_zero)
    {
        std::cout << "caught division by zero!\n";
    }
}
//]