Defining some stuff for each class in turn

Steven Schveighoffer schveiguy at yahoo.com
Fri Oct 2 09:45:20 PDT 2009


On Thu, 01 Oct 2009 13:53:46 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> Jarrett Billingsley wrote:
>> On Thu, Oct 1, 2009 at 12:25 PM, Andrei Alexandrescu
>> <SeeWebsiteForEmail at erdani.org> 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



More information about the Digitalmars-d mailing list