AJD wrote:
Hi,
I've been trying out boost.proto lately and I've noticed that for my
expressions it tends to force my operands to be created on the stack.
<snip>
The way you were defining your vector terminals confused proto into
thinking they needed to be wrapped in your vector_expr wrapper. The
extra layer of indirection is what is flummoxing msvc's optimizer. I can
only assume you went through those contortions in order to preserve the
nice aggregate initialization for your vector terminals. Try the
following instead.
#include <cstddef>
#include <iostream>
#include
#include
using namespace boost;
// This grammar describes which lazy vector expressions
// are allowed; namely, vector terminals and addition
// and subtraction of lazy vector expressions.
struct VectorGrammar
: proto::or_<
proto::and_<
proto::terminal< proto::_ >
, proto::if_< is_arraympl::_ > >
>
, proto::plus< VectorGrammar, VectorGrammar >
>
{};
// Expressions in the lazy vector domain must conform
// to the lazy vector grammar
struct VectorDomain;
// Here is an evaluation context that indexes into a lazy vector
// expression, and combines the result.
struct subscript_context
{
subscript_context(std::size_t i) : i(i)
{
}
// Use default_eval for all the operations ...
template< typename Expr, typename Tag = typename Expr::proto_tag >
struct eval : proto::default_eval< Expr, subscript_context const >
{
};
// ... except for terminals, which we index with our subscript
template< typename Expr >
struct eval< Expr, proto::tag::terminal >
{
typedef typename Expr::value_type result_type;
result_type operator ()(Expr const & expr, subscript_context
const & ctx) const
{
return proto::arg(expr)[ctx.i];
}
};
private:
std::size_t i;
};
// Here is the domain-specific expression wrapper, which overrides
// operator [] to evaluate the expression using the subscript_context.
template< typename Expr >
struct vector_expr
{
BOOST_PROTO_EXTENDS(Expr, vector_expr<Expr>, VectorDomain)
// Use the subscript_context to implement subscripting
// of a lazy vector expression tree.
typename proto::result_of::eval< Expr, subscript_context const >::type
operator [](std::size_t i) const
{
subscript_context const ctx(i);
return proto::eval(*this, ctx);
}
};
template< typename T, typename U = proto::is_proto_expr >
struct vector2;
template< typename T >
struct vector2
{
BOOST_PROTO_EXTENDS(typename proto::terminal::type,
vector2, VectorDomain)
typedef T value_type;
T & operator [](std::size_t i)
{
return proto::arg(*this)[i];
}
T const & operator [](std::size_t i) const
{
return proto::arg(*this)[i];
}
};
// Tell proto that in the VectorDomain, all
// expressions should be wrapped in vector_expr<>
struct VectorDomain
: proto::domain<
proto::pod_generator< vector_expr >
, VectorGrammar
>
{
};
int main()
{
vector2< float[3] > v1 = { 0, 1, 2 };
vector2< float[3] > v2 = { 3, 4, 5 };
vector2< float[3] > v3 = { 6, 7, 8 };
// Add two vectors lazily and get the 2nd element.
std::cout << (v1 + v2)[0];
return 0;
}
For me, the line "std::cout << (v1 + v2)[0]" generates:
; 107 : // Add two vectors lazily and get the 2nd element.
; 108 : std::cout << (v1 + v2)[0];
fldz
push ecx
fadd QWORD PTR __real@4008000000000000
fstp DWORD PTR tv227[esp+12]
fld DWORD PTR tv227[esp+12]
fstp DWORD PTR [esp]
call ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@M@Z ;
std::basic_ostream::operator<<
That looks pretty good to me.
HTH,
--
Eric Niebler
Boost Consulting
www.boost-consulting.com