Eric Niebler wrote:

> On 12/7/2010 2:37 PM, Thomas Heller wrote:
>> Hi,
>> 
>> I have been trying to extend a domain by "subdomaining" it. The sole
>> purpose of this subdomain was to allow another type of terminal
>> expression.
>> 
>> Please see the attached code, which is a very simplified version of what
>> I was trying to do.
> <snip>
> 
>> So, How to handle that correctly?
> 
> Yup, that's a problem. I don't have an answer for you at the moment,
> sorry.

I think i solved the problem. The testcase for this solution is attached.
Let me restate what I wanted to accomplish:

Given a domain, which serves as a common superdomain for certain different
subdomains.
The grammar associated with that domain will serve as the base grammar for
that EDSL.
There might be usecases, where certain proto expressions want to be allowed
in a sub domain, and additionally allowing that expression to be mixed with
the already defined grammar rules in the super domain.
So, in order to achieve that, the grammar of our common super domain needs
to be parametrized on a Grammar type. This will allow to reuse that
grammar, and extend it with other rules.
The implementation looks like this:

struct super_grammar
   : proto::switch_<super_grammar>
{
    template <typename Tag, typename Grammar = super_grammar>
    struct case_;
};

With the different case_ specializations on Tag we can define what valid
expressions are, with respect to the Grammar type. This defaults to
super_grammar.

To extend that grammar in terms of allowing additional expressions, but do
not change what expressions super_grammar matches we can do the following:

struct sub_grammar
   : proto::switch_<sub_grammar>
{
    template <typename Tag, typename Grammar = sub_grammar>
    struct case_ : super_grammar::case_<Tag, sub_grammar>
    {};
};

So far so good. With this technique, every expression which was valid in
super_grammar are now valid in sub_grammar with the addition of the
extensions.

This might be what people refer to as type-2 grammars.

Now, super_grammar belongs to super_domain and sub_grammar to sub_domain,
which is a sub domain of super_domain.
At the end of the day, I want to mix expressions from super_domain with
expressions from sub_domain.
The default operator overloads are not suitable for this, cause the deduced
domain of super_domain and sub_domain is super_domain.
This makes expressions of the form t1 OP t2 invalid (where t1 is from
super_domain and t2 from sub_domain) because t1 OP t2 is not a valid
expression in super_domain. However it is in sub_domain.
In this case we do not want the deduced_domain, but the strongest domain,
which is basically the most specialised domain, or strongest domain as I
tagged it.

I hope that makes sense.

Regards,

Thomas
#include <boost/proto/proto.hpp>

namespace proto = boost::proto;
namespace mpl = boost::mpl;

typedef proto::terminal<int> int_terminal;
typedef proto::terminal<double> double_terminal;

////////////////////////////////////////////////////////////////////////////////
template <typename Expr>
struct actor1;

struct grammar1;

// define our base domain
struct domain1
    : proto::domain<
        proto::pod_generator<actor1>
      , grammar1
      , proto::default_domain
    >
{};

// define the grammar for that domain
struct grammar1
    : proto::switch_<grammar1>
{
    // The actual grammar is parametrized on a Grammar parameter as well
    // This allows us to effectively reuse that grammar in subdomains.
    template <typename Tag, typename Grammar = grammar1>
    struct case_
        : proto::or_<
            proto::plus<Grammar, Grammar>
          , int_terminal
        >
    {};
};

// boring expression wrapper
template <typename Expr>
struct actor1
{
    BOOST_PROTO_BASIC_EXTENDS(Expr, actor1<Expr>, domain1)
};
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
template <typename Expr>
struct actor2;

struct grammar2;

// our domain2 ... this is a subdomain of domain1
struct domain2
    : proto::domain<
        proto::pod_generator<actor2>
      , grammar2
      , domain1
    >
{};

// the grammar2
struct grammar2
    : proto::switch_<grammar2>
{
    // again parameterized on a Grammar, defaulting to grammar2
    // This is not really needed here, but allows to reuse that grammar as well
    template <typename Tag, typename Grammar = grammar2>
    struct case_
        : proto::or_<
            // here we go ... reuse grammar1::case_ with our new grammar
            grammar1::case_<Tag, Grammar>
          , double_terminal
        >
    {};
};

// boring expression wrapper
template <typename Expr>
struct actor2
{
    BOOST_PROTO_BASIC_EXTENDS(Expr, actor2<Expr>, domain2)
};
////////////////////////////////////////////////////////////////////////////////

actor1<int_terminal::type> t1 = {{42}};
actor2<double_terminal::type> t2 = {{3.14}};


////////////////////////////////////////////////////////////////////////////////
// specialize is_extension trait, so actor1 and actor2 don't get picked up
// by proto's default operators.
// otherwise, expression like t1 + t2 would not work.
namespace boost { namespace proto {
    template <typename T>
    struct is_extension<actor1<T> > : mpl::false_ {};

    template <typename T>
    struct is_extension<actor2<T> > : mpl::false_ {};
}}

// define a trait which marks the actors as special classes
template <typename T>
struct is_actor : boost::mpl::false_ {};

template <typename T>
struct is_actor<actor1<T> > : boost::mpl::true_ {};

template <typename T>
struct is_actor<actor2<T> > : boost::mpl::true_ {};

template <typename Domain0, typename Domain1>
struct is_super_domain;

template <typename Domain0, typename Domain1>
struct is_super_domain
    : mpl::eval_if<
        boost::is_same<Domain0, typename Domain1::proto_super_domain>
      , mpl::true_
      , is_super_domain<Domain0, typename Domain1::proto_super_domain> 
    >
{};

template <typename Domain0>
struct is_super_domain<Domain0, Domain0>
    : mpl::true_
{};

template <typename Domain0>
struct is_super_domain<Domain0, proto::no_super_domain>
    : mpl::false_
{};


// strongest domain selects the most specialised domain
// meaning that if:
//  1. D0 is a super domain of D1, D1 is the strongest
//  2. D1 is a super domain of D0, D0 is the strongest
//  3. if 1 and 2 are false, the common domain is selected.
template <typename D0, typename D1>
struct strongest_domain2_impl
    : boost::mpl::eval_if<
        typename is_super_domain<D0, D1>::type//boost::is_same<D0, typename D1::proto_super_domain>
      , boost::mpl::identity<D1>
      , boost::mpl::eval_if<
            typename is_super_domain<D1, D0>::type//boost::is_same<typename D0::proto_super_domain, D1>
          , boost::mpl::identity<D0>
          , proto::detail::common_domain2<D0, D1>
        >
    >
{};

template <typename E0>
struct strongest_domain1
    : proto::domain_of<E0>
{};

template <typename E0, typename E1>
struct strongest_domain2
    : strongest_domain2_impl<
        typename proto::domain_of<E0>::type
      , typename proto::domain_of<E1>::type
    >
{};


////////////////////////////////////////////////////////////////////////////////
// Extension to proto to select the strongest domain, as described below.
struct strongest_domain
    : proto::domain<
        proto::detail::not_a_generator
      , proto::detail::not_a_grammar
      , proto::detail::not_a_domain
    >
{};

namespace boost { namespace proto {
    namespace detail
    {
        template <typename Trait, typename Tag, typename Expr>
        struct enable_unary<strongest_domain, not_a_grammar, Trait, Tag, Expr>
            : enable_unary<
                typename strongest_domain1<Expr>::type
              , typename strongest_domain1<Expr>::type::proto_grammar
              , Trait
              , Tag
              , Expr
            >
        {};

        template <typename Trait, typename Tag, typename Left, typename Right>
        struct enable_binary<strongest_domain, not_a_grammar, Trait, Tag, Left, Right>
            : enable_binary<
                typename strongest_domain2<Left, Right>::type
              , typename strongest_domain2<Left, Right>::type::proto_grammar
              , Trait
              , Tag
              , Left
              , Right
            >
        {};

        template <typename Tag, typename A0>
        struct make_expr_<Tag, strongest_domain, A0>
            : make_expr_<
                Tag
              , typename strongest_domain1<A0>::type
              , A0
            >
        {};

        template <typename Tag, typename A0, typename A1>
        struct make_expr_<Tag, strongest_domain, A0, A1>
            : make_expr_<
                Tag
              , typename strongest_domain2<A0, A1>::type
              , A0
              , A1
            >
        {};
    }
}}
////////////////////////////////////////////////////////////////////////////////

// define operators for that kind of classes, we do not want the deduced domain
// but the strongest domain.
// Meaning:
//   For "actor1<Expr> + actor2<Expr>" the result must be in domain2, not domain1
//   as deduce_domain would calculate the resulting domain.
//   If the resulting domain is domain1, the resulting expression would not match
//   the grammar of domain1 ...
BOOST_PROTO_DEFINE_OPERATORS(is_actor, strongest_domain)
////////////////////////////////////////////////////////////////////////////////

template <typename T>
void test(T const & t)
{
    std::cout << typeid(T).name() << "\n";
    std::cout << std::boolalpha;
    std::cout << "    matches grammar1? " << proto::matches<T, grammar1>::type::value << "\n";
    std::cout << "    matches grammar2? " << proto::matches<T, grammar2>::type::value << "\n";
}

template <typename T0, typename T1>
void test(T0 const& t0, T1 const& t1)
{
    test(t0 + t1);
    test(t1 + t0);
    std::cout << "\n";
}

template <typename T0, typename T1, typename T2>
void test(T0 const& t0, T1 const& t1, T2 const& t2)
{
    test(t0 + t1, t2);
    test(t1 + t0, t2);
    test(t1 + t2, t0);
    test(t2 + t1, t0);
    test(t0 + t2, t1);
    test(t2 + t0, t1);
    std::cout << "\n";
}

int main()
{
    test(t1);
    test(t2);
    test(t1, t1);
    test(t2, t2);
    test(t1, t2);
    test(t1, t1, t1);
    test(t2, t1, t1);
    test(t1, t2, t1);
    test(t1, t1, t2);
    test(t2, t2, t1);
    test(t2, t1, t2);
    test(t1, t2, t2);
    test(t2, t2, t2);
}

_______________________________________________
proto mailing list
proto@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/proto

Reply via email to