Kirit Thadaka
Kirit Thadaka

Reputation: 87

Can I give different behaviours to boost::proto::tag types?

I am trying to use boost proto to lazily evaluate expressions, what I want to do is be able to give different behaviours to tags like +, -, function etc.

function(
    terminal(8functionILi2EE)
  , plus(
        multiplies(
            terminal(6tensorILi0EE)
          , terminal(6tensorILi1EE)
        )
      , multiplies(
            terminal(6tensorILi2EE)
          , terminal(6tensorILi3EE)
        )
    )
)

For a tree like above, I want to be able to specify how each of the tree nodes should behave.

For eg.

struct context : proto::callable_context< context const >
{

    // Values to replace the tensors
    std::vector<double> args;

    // Define the result type of the zero.
    // (This makes the zero_context "callable".)
    typedef double result_type;

    // Handle the tensors:
    template<int I>
    double operator()(proto::tag::terminal, tensor<I>) const
    {
        std::cout << this->args[I] << std::endl;
        return this->args[I];
    }

    template<int I>
    void operator()(proto::tag::plus) const
    {
        std::cout << " + " << std::endl;
    }

};

When I do

double result = (_tensorA + _tensorB)(10, 20);

I expect my output to be

10
+
20

But it's just

10
20

Any help would be deeply appreciated! :)

Upvotes: 2

Views: 50

Answers (1)

sehe
sehe

Reputation: 393134

template<int I>
void operator()(proto::tag::plus) const
{
    std::cout << " + " << std::endl;
}

The template argument I is non-deducible, so the overload will never be applicable. Drop the template argument:

void operator()(proto::tag::plus) const
{
    std::cout << " + " << std::endl;
}

HOWEVER What you really want is intercept the binary operator. Well. Note it's binary. So it has two args:

template<size_t I, size_t J>
void operator()(proto::tag::plus, proto::literal<tensor<I>>&, proto::literal<tensor<J>>&) const {
    std::cout << " + " << std::endl;
}

Live On Coliru

However, this blocks further evaluation of the expression tree. Not what you wanted, right. So, let's do a simplisitic re-implementation:

template<size_t I, size_t J>
double operator()(proto::tag::plus, proto::literal<tensor<I>>& a, proto::literal<tensor<J>>& b) const {
    auto va = (*this)(proto::tag::terminal{}, a.get());
    std::cout << " + " << std::endl;
    auto vb = (*this)(proto::tag::terminal{}, b.get());
    return va + vb;
}

Live On Coliru

Generic, please

However, something tells me you wanted generic expressions. So t1 + (t2 + t3) should also work, but (t2 + t3) is no literal...

Let's simplify by delegating:

template<typename A, typename B>
double operator()(proto::tag::plus, A& a, A& b) const {
    auto va = proto::eval(a, *this);
    std::cout << " + " << std::endl;
    auto vb = proto::eval(b, *this);
    return va + vb;
}

Full Sample

Live On Coliru

#include <boost/proto/proto.hpp>
#include <vector>

namespace proto = boost::proto;

template <size_t N> struct tensor { };
template <size_t N, size_t M> tensor<N+M> operator+(tensor<N>, tensor<M>) { return {}; }

struct context : proto::callable_context< context const >
{
    using base_type = proto::callable_context<context const>;

    // Values to replace the tensors
    std::vector<double> args { 0, 111, 222, 333 };

    // Define the result type of the zero.
    // (This makes the zero_context "callable".)
    typedef double result_type;

    // Handle the tensors:
    template<size_t I>
    double operator()(proto::tag::terminal, tensor<I>) const
    {
        std::cout << this->args[I] << std::endl;
        return this->args[I];
    }

    template<typename A, typename B>
    double operator()(proto::tag::plus, A& a, B& b) const {
        auto va = proto::eval(a, *this);
        std::cout << " + " << std::endl;
        auto vb = proto::eval(b, *this);
        return va + vb;
    }
};

int main() {
    proto::literal<tensor<1> > t1;
    proto::literal<tensor<2> > t2;
    proto::literal<tensor<3> > t3;
    auto r = proto::eval(t1 + (t2 + t3), context());
    std::cout << "eval(t1 + (t2 + t3)) = " << r << "\n";
}

Prints

111
 + 
222
 + 
333
eval(t1 + (t2 + t3)) = 666

Upvotes: 1

Related Questions