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