Boost logo

Boost :

Subject: Re: [boost] [guidelines] why template errors suck
From: Smith, Jacob N (jacob.n.smith_at_[hidden])
Date: 2010-09-27 19:55:58


> -----Original Message-----
> From: boost-bounces_at_[hidden] [mailto:boost-
> bounces_at_[hidden]] On Behalf Of Eric Niebler
> Sent: Monday, September 27, 2010 11:06 AM
> To: boost_at_[hidden]
> Subject: Re: [boost] [guidelines] why template errors suck
>
> On 9/27/2010 1:16 PM, Smith, Jacob N wrote:
> >
> > Disclaimer: it has been a couple of years since I worked with
> concepts.
> >
> >
> > concept SpiritPlusesShortProtoTree<typename Node>
> > {
> > // Checks that the "ShortProtoTree" is the expression: (+X) +
> (+Y)
> > requires SpiritNode<Node>,
> > ShortProtoTree<Node>,
> > IsBinaryPlusOperator<Node>,
> > IsUnaryPlusOperator<get_child<_0,
> ProtoNode<Node>::children>::type>,
> > IsUnaryPlusOperator<get_child<_1,
> ProtoNode<Node>::children>::type>;
> > }
> >
> > I am waving my hands at the "get_child" functor; it is
> straightforward to describe using concepts.
> >
> > Concepts are fairly tightly related to algebra declarations. Any
> algebra can be thought of as a tree, therefore, any tree should be
> checkable via concepts. Disjoint ("or") concept requirements are not
> needed in this situation since the concept mechanism will choose the
> most specialized concept that the model satisfies, not the least
> specialized. [This is not to say that disjoint concepts aren't
> necessary in some cases.]
>
> You have shown that you can concept-check the expression (+X) + (+Y).
> You did it by traversing the tree in the SpiritPlusesShortProtoTree
> concept and verifying each node explicitly. I don't see (yet) how you
> can generalize this.

Do you want a concept checking the set of legal constructions of Spirit parsers or a concept checking a particular instance of the Spirit parsing framework? The first instance seems easier:

concept SpiritNode<typename Node>
{
        Iterator iterator = Node::iterator;
        typename children = Node::children; // opaque

        iterator parse(iterator, iterator, Node&);
}

concept SpiritTerminal<typename Node> : SpiritNode<Node> { }
 
concept SpiritUnaryNode<typename Node>
{
        requires SpiritNode<Node>;
        requires SpiritNode<get_child<_0, SpiritNode<Node>::children>::type>;
}

concept SpiritBinaryNode<typename Node>
{
        requires SpiritNode<Node>;
        requires SpiritNode<get_child<_0, SpiritNode<Node>::children>::type>,
                SpiritNode<get_child<_1, SpiritNode<Node>::children>::type>;
}

// etc. Althought I think this covers all the legal overloadable operators.

I'm not sure how "interesting" this is, other than to force a mapping to known arities for spirit nodes. In the second case, checking a particular instance of a grammar, let me take a short stab at it [this will assuredly have mistakes, but I don't think the fundamental solution is flawed modulo the "no concepts in C++" issue]:

S ::= E0
E0 ::= E1 + E0 | E1
E1 ::= <symbol>

concept MyGrammarS<typename Node>
{
        requires SpiritNode<Node>;
        // Semantic qualification:
        // It is not legal to define a direct modeling relationship
        // to MyGrammarS. All such relationships must occur
        // indirectly, i.e., through MyGrammarE0, MyGrammarE1, or MyGrammarE1pE0.
}

concept MyGrammarE0<typename Node>
{
        requires MyGrammarS<Node>;
}

concept MyGrammarE1<typename Node>
{
        requires SpiritNode<Node>;
}

concept MyGrammarE1pE0<typename Node>
{
        requires SpiritBinaryNode<Node>;
        requires MyGrammarE1<get_child<_0, SpiritNode<Node>::children>::type>,
                MyGrammarE0<get_child<_1, SpiritNode<Node>::children>::type>;
}

template <MyGrammarE0 Node>
concept_map MyGrammarS<Node> { }

template <MyGrammarE1 Node>
concept_map MyGrammarE0<Node> { }

// The following modeling relationship uses a made up syntax. I'm not sure
// anyone implemented a modeling relationship syntax from multisorted concepts
// without an underlying carrier.
template <MyGrammarS L, MyGrammarS R>
concept_map MyGrammarE0<MyGrammarE1pE0<L,R>> { }

Now when we define the modeling relationship from types to concepts, we do not allow an definition to the concept "MyGrammarS". For instance:

template <typename L, typename R> struct plus_node { };

template <MyGrammarS L, MyGrammarS R>
concept_map MyGrammarE1pE0<plus_node<L, R>> { }

template <int K> struct symbol_node { };

template <int K>
concept_map MyGrammarE1<symbol_node<K>> { }

The following type is legal:

typedef plus_node<plus_node<symbol_node<0>, symbol_node<1>>, plus_node<symbol_node<1>, symbol_node<2>>> my_gram0; // legal

But this type is illegal:

typedef plus_node<int, double> my_gram1; // illegal

Even if we have types that otherwise satisfy Spirit nodes:

struct foo { }; concept_map SpiritNode<foo> { /* impl */ }

They can't be used within my grammar:

typedef plus_node<foo, foo> my_gram2; // illegal

This of course relies on the fact that I don't try to "hide" MyGrammar* nodes by having them model MyGrammarS and not the fully specialized variant of the concept hierarchy for my node's type.

> You appear to be writing a new concept for each valid tree type. But
> there are an infinite number of trees that are valid Spirit trees (and
> an infinite number that are not). I still don't see how concepts can
> tell the difference between the two sets.
>
> --
> Eric Niebler
> BoostPro Computing
> http://www.boostpro.com
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk