Чтобы познакомиться с Boost.Proto, я пытаюсь создать еще одну библиотеку шаблонов выражений для векторов с плавающей запятой фиксированного, но произвольного размера, адаптируя пример TArray из руководства пользователя. Первым делом я определяю свой векторный класс:
typedef double real;
// Forward-declare an expression wrapper
template<typename Expr>
struct vector_expr_wrapper; // line 13
class FPVector : vector_expr_wrapper< proto::terminal< FPVector >::type > { // line 16
public:
FPVector() : numElements(0), elements(0) {}
FPVector(size_t n) : numElements(n), elements(new real[n]) {}
~FPVector() { delete[] elements; }
real& operator[](size_t i) { return elements[i]; }
template<typename Expr>
FPVector const& operator=(vector_expr_wrapper<Expr> vec_expr) {
for(size_t i=0; i<numElements; i++) {
elements[i] = vec_expr[i];
}
return *this;
}
private:
size_t numElements;
real * elements;
};
vector_expr_wrapper
также перегружает его operator[]
оценивать себя с vector_context
происходит от proto::callable_context
это возвращает vector[index]
за FPVector
терминалы.
Когда я компилирую свой код и вызываю его с очень простым выражением (a = b + c;
) Я получаю сообщение об ошибке:
../main.cpp:16:18: error: invalid use of incomplete type ‘struct vector_expr_wrapper<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<FPVector>, 0l> >’
../main.cpp:13:8: error: declaration of ‘struct vector_expr_wrapper<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<FPVector>, 0l> >’
../main.cpp: In function ‘int main()’:
../main.cpp:121:8: error: no match for ‘operator+’ in ‘b + c’
а затем g ++ перечисляет возможные варианты материала …
Из этого я понимаю, что я должен дать полное определение vector_expr_wrapper
перед определением FPVector
но я не могу этого сделать, потому что все остальное в vector_expr_wrapper
зависит от FPVector
(грамматика, контекст оценки …)
Как я могу решить это (то есть, как я должен расположить свои классы)?
Пример TArray обходит эту проблему — я предполагаю — очень поздно определяя класс массива и определяя его тип с помощью int[3]
до того, что я думаю, я не могу воспроизвести в моем контексте.
Большое спасибо за Вашу помощь!
Вот мое решение:
#include <vector>
#include <iostream>
#include <boost/proto/proto.hpp>
#include <boost/format.hpp>
namespace proto = boost::proto;
using proto::_;
typedef double real;
struct vector_grammar
: proto::or_<
proto::terminal< std::vector<real> >
, proto::plus< vector_grammar, vector_grammar >
, proto::minus< vector_grammar, vector_grammar >
, proto::multiplies< vector_grammar, vector_grammar >
, proto::divides< vector_grammar, vector_grammar >
>
{};
template<typename Expr>
struct vector_expr;
// Tell proto that in the vector_domain, all
// expressions should be wrapped in vector_expr<> and
// must conform to the vector_grammar
struct vector_domain
: proto::domain<
// use_basic_expr here instructs proto to use the stripped-
// down proto::basic_expr instead of proto::expr to reduce
// compile times. It's not necessary.
proto::use_basic_expr<proto::pod_generator<vector_expr> >
, vector_grammar
>
{};
struct vector_subscript_context
: proto::callable_context< vector_subscript_context const >
{
typedef real result_type;
explicit vector_subscript_context(std::ptrdiff_t i)
: i_(i)
{}
// Index array terminals with our subscript. Everything
// else will be handled by the default evaluation context.
real operator ()(proto::tag::terminal, std::vector<real> const &data) const
{
return data[this->i_];
}
private:
std::ptrdiff_t i_;
};
// Forward-declare an expression wrapper
template<typename Expr>
struct vector_expr
{
BOOST_PROTO_BASIC_EXTENDS(Expr, vector_expr<Expr>, vector_domain)
// Use the vector_subscript_context to implement subscripting
// of a vector_expr expression tree.
real operator []( std::ptrdiff_t i ) const
{
vector_subscript_context const ctx(i);
return proto::eval(*this, ctx);
}
};
template<typename = proto::is_proto_expr>
struct FPVector_
: vector_expr<
proto::basic_expr<
proto::tag::terminal
, proto::term< std::vector<real> >
>
>
{
explicit FPVector_(std::size_t n = 0)
{
proto::value(*this).resize(n);
}
real & operator[](std::ptrdiff_t i)
{
return proto::value(*this)[i];
}
real const & operator[](std::ptrdiff_t i) const
{
return proto::value(*this)[i];
}
template<typename Expr>
FPVector_ & operator=(vector_expr<Expr> const & that)
{
std::ptrdiff_t const size =
static_cast<std::ptrdiff_t>(proto::value(*this).size());
for(std::ptrdiff_t i = 0; i < size; ++i)
proto::value(*this)[i] = that[i];
return *this;
}
};
typedef FPVector_<> FPVector;
int main()
{
FPVector a(3), b(3), c(3);
for(std::ptrdiff_t i = 0; i < 3; ++i)
b[i] = c[i] = i;
a = b + c;
std::cout
<< boost::format("a = {%d, %d, %d}") % a[0] %a[1] %a[2]
<< std::endl;
}
Код, который вы показываете, имеет ряд проблем:
// Forward-declare an expression wrapper
template<typename Expr>
struct vector_expr_wrapper; // line 13
class FPVector : vector_expr_wrapper< proto::terminal< FPVector >::type >
Вы не можете наследовать от неполного шаблона класса (vector_expr_wrapper
) из (конкретного) класса (FPVector
). Кроме того, ваш FPVector
Тип пытается содержать копию себя. proto::terminal
является оберткой вокруг типа объекта Думайте об этом как о замене самой вещи, но с некоторой дополнительной прото-специей. Тогда наследование от него или от чего-то, что наследует от него, не полетит.
Единственный реальный трюк в опубликованном мною коде — это использование proto::is_proto_expr
, Это неприятный хак, чтобы заставить ADL запустить поиск перегрузок операторов, определенных в boost::proto
Пространство имен. Это подробно описано в предупреждении внизу «Расширяет<Раздел «Выражение выражения» из прото документов.
Других решений пока нет …