There seems to be a significant problem with the unary function node (and by that I mean (*this)() ) generated by proto::extends and BOOST_PROTO_EXTENDS_USING_FUNCTION().

Indeed, it instantiates the generator before the expression type is complete, leading to very confusing ODR issues and a whole can of worms.

Usually the generator is dumb, so you don't run into this issue, but it's a serious problem for DSELs that wish to do significant work in the generator (such as tagging nodes with some semantic compile-time or runtime information).

Removing the operator from the grammar doesn't seem to do anything, the only workaround I've found is to special case the generator to avoid doing anything.

Attached in an example of code that just goes very wrong. Sorry for the terrible code, I couldn't think of anything simpler to instantiate proto::is_expr only for certain tag types.

The generator instantiates proto::is_expr on the user-defined expression type. Since at the point it gets instantiated, that type is incomplete, it ends up returning false. This leads to the expression construction mechanism getting confused, doing a compile-time infinite loop.

Change the first #if 1 to #if 0 so that is_expr is not instantiated for function nodes. Notice how calling is_expr on the expression generated for plus works just fine. The error can also be fixed by changing the second #if 1 to #if 0, it uses BOOST_PROTO_BASIC_EXTENDS to entirely avoid disable function nodes.

It would be nice if this issue could be resolved or documented, or if a proper way to disable that operator with proto::extends was possible.
#include <boost/proto/proto.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_same.hpp>

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

template<class Expr, class Dummy = proto::is_proto_expr>
struct my_expr;

struct generator
  : proto::transform<generator>
{
  template<class Expr, class State, class Data>
  struct impl : proto::transform_impl<Expr, State, Data>
  {
    typedef typename impl::expr expr;
    typedef typename proto::tag_of<expr>::type tag;
    typedef my_expr<expr> type;
    
#if 1
    typedef boost::proto::tag::plus tag_for_which_is_expr_should_be_avoided;
#else
    typedef boost::proto::tag::function tag_for_which_is_expr_should_be_avoided;
#endif
    
    typedef typename mpl::
    if_< mpl::
         and_< mpl::
               not_< boost::is_same<tag, tag_for_which_is_expr_should_be_avoided> >
             , proto::is_expr<type>
             >
       , type
       , type
       >::type                  result_type;
    
    result_type operator()( typename impl::expr_param e
                          , typename impl::state_param
                          , typename impl::data_param
                          ) const
    {
      return result_type(e);
    }
  };
};

struct domain
  : proto::domain<generator>
{
};

template<class Expr, class Dummy>
struct my_expr
#if 1
  : proto::extends<Expr, my_expr<Expr>, domain>
{
    typedef proto::extends<Expr, my_expr<Expr>, domain> parent;
    my_expr(Expr const& expr = Expr()) : parent(expr)
#else
{
    BOOST_PROTO_BASIC_EXTENDS(Expr, my_expr<Expr>, domain);
    my_expr(Expr const& expr = Expr()) : proto_expr_(expr)
#endif
    {
    }
};

template<class T, class Dummy = proto::is_proto_expr>
struct my_terminal
  : my_expr< typename proto::terminal<T>::type >
{
};

int main()
{
  my_terminal<int> i, j;
  i + j;
}
_______________________________________________
proto mailing list
proto@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/proto

Reply via email to