boost.proto + обнаруживает неверный терминал перед построением дерева выражений
Я играл с Boost.Proto, в основном для удовольствия, и посмотреть, смогу ли я в будущем использовать его в своих собственных проектах. Тем не менее, как и большинство новичков в этой библиотеке, я играл с модифицированной версией примера "ленивый вектор", но для выполнения оценки использовал преобразования вместо контекстов. Вектор определяется следующим образом (хорошо, я знаю, "вектор" не подходит для чего-то определенного в глобальной области имен...)
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= для принятия деревьев выражений, как это обычно делается в подобных вещах). Я определил скаляры, используя код из руководства прото
// 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. В итоге я получил следующий рабочий код
// 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;
}
Вопрос заключается в следующем: поскольку размерность массивов уже закодирована в выражении, то должна быть возможность обнаружить недопустимую комбинацию уже во время компиляции. Должно быть даже возможно избежать создания дерева в первую очередь. Как это достигается?
Спасибо заранее, с наилучшими пожеланиями
1 ответ
Очень хорошо. Теперь вам нужно определить грамматику, которая принимает только допустимые векторные выражения, примерно так:
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
следующее:
struct vector_domain
: proto::domain <proto::generator <vector_expr>, vector_grammar >
{};
Это должно удерживать вас от создания выражений, которые не проходят пользовательскую проверку типов. Второй параметр шаблона для proto::domain
это грамматика, которой должны соответствовать все выражения в этой области.
Отказ от ответственности: приведенный выше код не проверен, но он должен заставить вас двигаться в правильном направлении.