Question

I've got a data object that I'm trying to get all of the operators working with. It's one chunk of data with variable ptrs into it, and has any number of different types and sizes and whatnot. Types are handled with enums and templates and switch statements. So for each x, d[x] is one type, with any number of them and they can be vectors. so d[x][y] and d[x][y][z]. I made an internal helper object to help with this. So I have [] overloaded to do things like this, and it'll return the correct type fine: (gcc 4.6.1)

[Edit: I have the same trouble with d(x,y,z)--the problem isn't the [] operator]

int i = d[0][3][5];

I'm overloading T() in this helper object.

template <class T> 
data::helper::operator T ();            // switch(x)...return different types
data::helper data::operator [] (int i); // recurse, return helper(t, d, x, i, j);

So I'll just return this object, which resolves it's type at that point (switch with cases tied to t->get< char>(d, x, i, j), etc.). So the problem is, if I want to do anything like this

int i = d[0][1] + d[4][2];
if (d[5][1] != d[3][0]) ...

then I ended up having to overload every operator to take in this temporary array helper object. And now I'm running into having to make a temporary value in there sometimes for some of the operators, which is a pain.

Basically, I feel like I need the operator T() to resolve first, before the compiler tries to take two of these and add them.

I have to do this anyway for the = and +=, etc. operators, but I'd like to delete these jazillion macros helping me define all of these other operators.

Also, I feel like if I could overload the lvalue operator somehow, I could not worry about the = operator. Maybe that and &() (which right now just returns a templated ptr). ...? Or actually, this is more what I mean, at least for d[] = something, but I haven't got this to work. I'm not sure how to convert a ptr of any type to this return value.

data::helper & data::operator [] (int i);

I have most of this working but it's a lot of code, and I think I'm going to have to add an extra if statement to every access to do the temp stuff, which I don't want to do. So what did I miss?

Edit: using d(x,i,j) is the same as d[x][i][j]. I'm pretty sure I'm doing at least the beginning part of what's being used in the link n.m. posted. The problem is resolving that last helper object into its data before it's used in a statement. Somehow the compiler wants an operator that accepts the helper object even though it knows how to resolve it when it's alone... I think. Been a couple days into overloading every operator so I forget all the details. :)

But the main problem now is with stuff like this:

helper operator + (helper & l, helper & r)

I would like to define the following but it's not getting used--then I think my problems might be solved. similar story for unary ops ~, -, and postfix ++, --.

template <class T> T operator + (helper & l, helper & r)

But all of this is just because there's something off about my T(), I think. Most of this is new to me, so I bet I'm missing something.

Was it helpful?

Solution

The practical way to do this kind of thing is with expression templates.

I'd change your return values from operator[] to an expression template even.

This will use C++11 features, because it makes it shorter.

enum class ExpressionType { Index, Addition };
template< ExpressionType Op, typename LHS, typename RHS >
struct Expression {
  LHS lhs;
  RHS rhs;
  template<typename T>
  operator T();
};
// to separate out the evaluation code:
template< typename T, ExpressionType Op, typename LHS, typename RHS >
struct Evaluate {
  T operator()( Expression<Op, LHS, RHS> exp ) const;
};
template< ExpressionType Op, typename LHS, typename RHS >
template<typename T>
Expression<Op,LHS,RHS>::operator T() {
  return Evaluate<T,Op,LHS,RHS>()( std::move(*this) );
}
// further specializations needed:
template< typename T, typename RHS >
struct Evaluate< T, ExpressionType::Index, data, RHS > {
  T operator()( Expression<Op, ExpressionType::Index, data, RHS> exp ) const {
    // we just assume RHS can be treated like an integer.  If it cannot,
    // we fail to compile.  We can improve this with SFINAE elsewhere...
    return exp.lhs.get_nth(exp.rhs);
  }
};
template< typename T, typename LHS, typename RHS >
struct Evaluate< T, ExpressionType::Addition, LHS, RHS > {
  T operator()( Expression<Op, ExpressionType::Index, data, RHS> exp ) const {
    // code with all of LHS, RHS and T visible!
  }
};
template<typename E>
struct is_expression : std::false_type {};
template<ExpressionType Op, typename LHS, typename RHS>
struct is_expression<Expression<Op,LHS,RHS> : std::true_type {};
template<ExpressionType Op, typename LHS, typename RHS>
Expression<Op, LHS, RHS> make_expression( LHS&& lhs, RHS&& rhs ) {
  return { std::forward<LHS>(lhs), std::forward<RHS>(rhs) };
}
// here is why I want to start out with returning an expression.  This SFINAE test
// is extremely easy because of that -- we overload operator+ on any two types, so long
// as one of them is an Expression!
template<typename LHS, typename RHS, typename=typename std::enable_if<is_expression<LHS>::value || is_expression<RHS>::value >::type>
ExpressionType<ExpressionType::Addition, LHS, RHS> operator+( LHS&& lhs, RHS&& rhs )
{
  return make_expression<ExpressionType::Addition>(std::forward<LHS>(lhs), std::forward<RHS>(rhs) );
}

so the idea is, we build at compile time a tree of templates that represent the order in which various expressions are evaluated by the compiler.

When we finally cast it to a concrete type T, only then do we start the evaluation work.

This avoids having to create any temporaries, but does mean we have to do a lot of template mojo in order to get things up and running. The above is a sketch of such an template expression tree generator.

To see a complete implementation of a simple case, here is a link to wikipedia's article on the subject, where a full blown expression tree system is built up to do std::vector vector processing without temporaries.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top