www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - proxies

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Proxying is a useful technique for achieving nice effects like e.g. 
expression templates. Consider, for example, a matrix operators package 
that returns "lazy" matrices in the form of expression templates:

struct Matrix
{
     LazyMatrix!("a + b") opAdd(ref Matrix another) { ... }
     ...
}

Now if Matrix also defines construction and assignment from LazyMatrix, 
it's all fine and dandy because we can write:

Matrix a, b;
Matrix c = a + b; // no temporary matrix created!
a = b + c;        // no temporary matrix created!

The problem is that auto works against all this:

Matrix a, b;
auto c = a + b; // c is _not_ a Matrix!

Using c directly has any number of unpleasant surprises in store for the 
unwitting user. For example, modifying a and b also modifies c. And 
since c does the addition on the fly, using c[i, j] directly will 
actually be slower than using a matrix!

How could this effect be avoided? In the past I suggested that proxy 
objects should be private and offer opImplicitCast to the desired types. 
Then the compiler figures that out and generates code appropriately.

Another solution would be to define opAuto that dictates what happens 
when an automatic object is instantiated from the proxy type:

struct LazyMatrix(alias expression)
{
     Matrix opAuto() { ... }
}

Then writing:

auto c = a + b;

will automatically make c a Matrix and initialize it as prescribed by 
opAuto. On the other hand, if the type is explicitly requested, the 
proxy stays in vigor:

LazyMatrix!("a + b") cproxy = a + b;

This is, of course, useful when code is interested in capturing the 
proxy itself.

One problem I see with this is template instantiation:

void f(T)() { ... }
f(a + b);

What should T be? LazyMatrix or Matrix? Another problem is, what does 
typeof do?

typeof(a + b) c = a + b;

Should that be same as auto, or should typeof tell the truth? Any way 
you choose, some of the current assumptions will break. So I'm hoping 
someone here will come with a better idea.


Andrei
Jan 25 2009
parent Jason House <jason.james.house gmail.com> writes:
Andrei Alexandrescu wrote:

 Proxying is a useful technique for achieving nice effects like e.g.
 expression templates. Consider, for example, a matrix operators package
 that returns "lazy" matrices in the form of expression templates:
 
 struct Matrix
 {
      LazyMatrix!("a + b") opAdd(ref Matrix another) { ... }
      ...
 }
 
 Now if Matrix also defines construction and assignment from LazyMatrix,
 it's all fine and dandy because we can write:
 
 Matrix a, b;
 Matrix c = a + b; // no temporary matrix created!
 a = b + c;        // no temporary matrix created!
 
 The problem is that auto works against all this:
 
 Matrix a, b;
 auto c = a + b; // c is _not_ a Matrix!
 
 Using c directly has any number of unpleasant surprises in store for the
 unwitting user. For example, modifying a and b also modifies c. And
 since c does the addition on the fly, using c[i, j] directly will
 actually be slower than using a matrix!
 
 How could this effect be avoided? In the past I suggested that proxy
 objects should be private and offer opImplicitCast to the desired types.
 Then the compiler figures that out and generates code appropriately.
 
 Another solution would be to define opAuto that dictates what happens
 when an automatic object is instantiated from the proxy type:
 
 struct LazyMatrix(alias expression)
 {
      Matrix opAuto() { ... }
 }
 
 Then writing:
 
 auto c = a + b;
 
 will automatically make c a Matrix and initialize it as prescribed by
 opAuto. On the other hand, if the type is explicitly requested, the
 proxy stays in vigor:
 
 LazyMatrix!("a + b") cproxy = a + b;
 
 This is, of course, useful when code is interested in capturing the
 proxy itself.
 
 One problem I see with this is template instantiation:
 
 void f(T)() { ... }
 f(a + b);
 
 What should T be? LazyMatrix or Matrix? Another problem is, what does
 typeof do?
 
 typeof(a + b) c = a + b;
 
 Should that be same as auto, or should typeof tell the truth? Any way
 you choose, some of the current assumptions will break. So I'm hoping
 someone here will come with a better idea.
 
 
 Andrei

What if a+b has a polysemous return type of Matrix or LazyMatrix, with a default of Matrix? That should operate like string literals. If Matrix was implicitly castable to LazyMatrix (but not vice versa), then operations could be written based on LazyMatrix. The user could pass in Matrix and it'd work, and function calls with a polysemous value would use LazyMatrix instead of Matrix to avoid the implicit cast. Of course, that solution sounds like it requires expanded compiler support for polysemous values and requires LazyMatrix to not be implicitly castable to Matrix. I also don't know if implictly casting Matrix to LazyMatrix makes sense.
Jan 25 2009