On Thu, 01 Oct 2009 13:53:46 -0400, Andrei Alexandrescu <[email protected]> wrote:

Jarrett Billingsley wrote:
On Thu, Oct 1, 2009 at 12:25 PM, Andrei Alexandrescu
<[email protected]> wrote:
[code injection]
What do you think?
 I think it sounds interesting enough, but I can't help but wonder if
this is a feature that you've really thought through (especially wrt.
how it interacts with mechanisms such as template mixins and normal
symbol inheritance), or if you just want it to support some pattern
you want to use in Phobos 2.

I've known for a long time this was in store if we want to define decent reflection. It's also been a perennial source of trouble with a lot of code that needs to inject members.

Let me give another example. When we discussed opCmp around here, people said that's the old way of doing things and that the right way is to use an interface Comparator!T, akin to Java's Comparator<T>:

http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html

That only solves exactly one level of inheritance. It's a more elaborate solution that doesn't quite help that much. Consider:

interface Comparator(T)
{
     int opCmp(T rhs); // yum, exact type
}

class Widget : Comparator!Widget
{
     override opCmp(Widget rhs) { ... } // yum, exact type
}

class Gadget : Widget
{
     override opCmp(Gadget rhs) { ... } // ERROR!!!!!!!!
}

Of course that didn't work. Gadget.opCmp doesn't override anything. Gadget needs to remember to inherit Comparator!Gadget:

class Gadget : Widget, Comparator!Gadget
{
     override opCmp(Gadget rhs) { ... } // oh ok
}

So any class X in Widget's hierarchy that forgets to inherit Comparator!X undergoes the risk of having the wrong opCmp called for it.

With injection, Comparator can be made to work for any interface:

interface Comparator(T)
{
     int opCmp(Comparator!T rhs);
     mixin(Impl) // for each implementation Impl
     {
         int opCmp(Impl rhs);
         override int opCmp(Comparator!T rhs)
         {
             return opCmp(cast(Impl) rhs);
         }
     }
}

class Widget : Comparator!Widget { ... }

Now every derived class of Widget, including Widget itself, commits to define a opCmp with the proper signature (failure to do so results in a linker error), and also defines the interface function in terms of it.

This still isn't optimal. For example, two different derivatives of the same class could try to compare to eachother and end up passing null into your opCmp. It would be nice if the compiler could reject unrelated comparisons:

class X: Comparator!X
{
  int opCmp(X) {}
}

class Y: X
{
  int opCmp(Y) {}
}

class Z: X
{
  int opCmp(Z) {}
}

void main()
{
  auto y = new Y;
  auto z = new Z;
  z < y; // error, rejected because they can't possibly be related
}

If there was a way to do that it would be cool. I also think the auto-defined opCmp should look like this:

override int opCmp(Comparator!T rhs)
{
  if(auto imp = cast(Impl) rhs)
     return opCmp(imp);
  return false;
}

That would cut down on calls into your actual opCmp function with a null argument.

-Steve

Reply via email to