boost.proto + 在构建表达式树之前检测无效终端 [英] boost.proto + detect invalid terminal before building the expression tree

查看:25
本文介绍了boost.proto + 在构建表达式树之前检测无效终端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在玩 Boost.Proto,主要是为了好玩,看看将来我是否可以在我自己的项目中使用它.那说,作为这个库的大多数初学者,我玩过懒惰向量"示例的修改版本,但使用转换而不是上下文来执行评估.向量定义如下(好吧,我知道,对于在全局命名空间范围内定义的东西来说,'vector' 不是一个好名字......)

I was playing with Boost.Proto, mostly for fun and to see if in future I could make some use of it in my own projects. That said, as probably most beginners of this library, i've played with a modified version of the 'lazy vector' example, but using transforms instead of contexts to perform the evaluation. The vector is defined as follows (ok, i know, 'vector' is not a good name for something defined at the global namespace scope...)

template <std::size_t D, class T>
class vector { 
    T data_[D];
    enum { dimension = D };
    // Constructors, destructors...
};

// expression wrapper
template <class> class vector_expr;

它在维度和数据类型上进行了模板化,类似于 boost::array (我没有使用它,因为我想重载 operator= 以接受表达式树,就像在这类事情中通常所做的那样).我使用 proto 手册中的代码定义了标量

it is templated on the dimension and the data type, kind of boost::array (i did not use that since i'd like to overload operator= to accept expression trees, as usually done in this sort of things). I defined scalars using code from proto's manual

// scalar = everything convertible to double
struct scalar_terminal :
    proto::terminal<proto::convertible_to <double> >
{};

// vector = everything for which the is_vector returns true_
template <class T> struct is_vector : mpl::false_ {};
template <std::size_t D, class T> struct is_vector <vector <D, T> > : mpl::true_ {};

struct vector_terminal :
    proto::and_ <
       proto::terminal<_>
     , proto::if_<is_vector<proto::_value>()>
  >
{};

// domain   
struct vector_domain
    : proto::domain <proto::generator <vector_expr> >
{};

// expression wrapper
template <class Expr>
struct vector_expr : proto::extends <Expr, vector_expr <Expr>, vector_domain>
{
    typedef proto::extends <Expr, vector_expr <Expr>, vector_domain> base_type;

    // Construct from expression (enough to compile)
    vector_expr (Expr const &e) : base_type (e) {}
};

// Bring in operators
BOOST_PROTO_DEFINE_OPERATORS(is_vector, vector_domain)

现在,我想做的第一件事是:检查表达式中的所有向量终结符是否都相同维度 D.我最终得到了以下工作代码

Now, the first thing i wanted to do is: check if all vector terminals in an expression have the same dimension D. I ended up with the following working code

// a meta-function that returns the vector dimension
template <class T>
struct vector_dim
{
    typedef mpl::int_ <T::dimension> type;
};

// a meta-function that combines dimensions from subtrees. int<-1> means
// that sub-trees store vectors of differing static dimension. No good.
template <class D1, class D2>
struct dim_combine
{
   typedef mpl::int_ < -1 > type;
};

// ok, dimensions are the same, propagate up the value
template <class D>
struct dim_combine <D, D>
{
   typedef D type;
};

// 0 is used to mark scalars. It is ok to mix vectors and scalars
// but propagate up the vector dimension only. This is for vector
// on the left and scalar on the right.
template <class D>
struct dim_combine <D, mpl::int_ <0> >
{
   typedef D type;
};

// this is for scalar on the left, vector to the right of some 
// binary operator.
template <class D>
struct dim_combine <mpl::int_ <0>, D>
{
   typedef D type;
};

// need this too to avoid ambiguity between the two specializations
// above when D is int_ <0>. Even if this combination should never
// happen
template <>
struct dim_combine <mpl::int_ <0>, mpl::int_<0> >
{
   typedef mpl::int_<0> type;
};


// A transform that check that all arrays have the same dimension
struct vec_dim_check
    : proto::or_ <
        proto::when <
            vector_terminal
          , vector_dim<proto::_value>()
        >
      , proto::when <
            scalar_terminal
          , boost::mpl::int_<0>()
        >
      , proto::when <
            proto::nary_expr<_, proto::vararg<_> >
          , proto::fold<_, boost::mpl::int_<0>(), dim_combine<vec_dim_check, proto::_state>()>
        >
    >
{};

template <class E>
void check_dim (E const&)
{
    typedef typename boost::result_of<vec_dim_check(E)>::type type;
    BOOST_ASSERT(type::value == 3);
}

int main (int, char**)
{
    vector <3,double> a,b,c;
    check_dim (2*a+b/c);
    return 0;
}

问题是:既然数组的维度已经在表达式中编码,那么它应该可以在编译时检测到无效组合.甚至应该可以避免首先创建树.这是如何实现的?

The question is: since the dimension of the arrays is already encoded in the expression, then it should be possible to detect invalid combination already at compile time. It should be even possible to avoid creating the tree in the first place. How is this achieved ?

提前致谢,最好的问候

推荐答案

非常好.现在您需要定义一个只接受有效向量表达式的语法,如下所示:

Very nice. Now you need to define a grammar that only accepts valid vector expressions something like this:

struct vector_grammar_untyped
  : proto::or_<
        scalar_terminal,
        vector_terminal,
        proto::nary_expr<proto::_, proto::vararg<vector_grammar_untyped> >
    >
{};

struct vector_grammar
  : proto::and_<
        vector_grammar_untyped,
        proto::if_<mpl::not_equal_to< mpl::int_<-1>, vec_dim_check >()>
    >
{};

然后,您将 vector_domain 的定义更改如下:

Then, you change your definition of vector_domain as follows:

struct vector_domain
    : proto::domain <proto::generator <vector_expr>, vector_grammar >
{};

这应该可以防止您创建未通过自定义类型检查的表达式.proto::domain 的第二个模板参数是该域中的所有表达式都必须遵守的语法.

That should keep you from creating expressions that don't pass your custom type-checking. The second template parameter to proto::domain is the grammar to which all expressions in that domain must conform.

免责声明:以上代码未经测试,但应该能让您朝着正确的方向前进.

Disclaimer: The above code is untested, but it should get you moving in the right direction.

这篇关于boost.proto + 在构建表达式树之前检测无效终端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆