On 10/31/06, Marvin Humphrey <[EMAIL PROTECTED]> wrote:

On Oct 23, 2006, at 6:44 PM, David Balmain wrote:

>  Another really nice result of this approach is that
> we can easily apply filters to any method call, simply by swapping out
> the macro with a function. So for example, if we want our animal to
> breath in before it speaks (best example I would think of in this case
> :P):
>
> #define Animal_Speak(self) Animal_speak((Animal *)self)
>
> void Animal_speak(Animal *self)
> {
>    self->m->breath_in(self); // call Animal's private breat_in method
>    self->m->speak(self);
> }

There are some limits to our flexibility with these macrofied method
calls, though.

First, the macros evaluate their arguments twice, which is a problem
if the arguments happen() to cause any side_effects++.

     Dog_Chase_Cat(dogs[i++]); /* uh-oh. */

<http://gcc.gnu.org/onlinedocs/cpp/Duplication-of-Side-Effects.html>

For core development, I'm not worried -- committers will be expected
to catch such problems.  However, if we ever try to design a full-on
public C interface, as opposed to a semi-public interface for
bindings authors, I'd consider those unsafe macros a design flaw.

Second, while playing around with this idea I've realized that if all
the macros follow the pattern I'd originally proposed, we'd be giving
up a hell of a lot of type-checking.  "self" has to be cast on _both_
the left and the right hand side, or the compiler will complain at
some point.


Casting on both sides for all method calls basically means treating
everything, everywhere as a void*.  Bad.

     #define Animal_Speak(self) ((Animal*)self)->speak((Animal*)self)

     Animal_Speak(dirty_old_shoe);  /* no compile-time warning! */


But then, without the double-cast, this function is problematic
regardless of whether you use Ferret-style or KinoSearch-style
inheritance.

     void
     Dog_chase_cat(XXXXX *self)
     {
         Animal_Run(self);
         Dog_Bark(self);
     }

With Ferret-style inheritance, we cast the left-hand side but not the
right:

     #define Animal_Run ((Animal*)self)->run(self);

If "self" is a Dog*, the compiler complains, because the "run" method
expects an Animal* as its first argument and we've given it a Dog*.
If "self" is an Animal*... well, then Dog_Bark will cause a compile-
time error because Animal doesn't have a "bark" method.

We're similarly screwed with KS-style inheritance, where we cast the
right-hand side:

      #define Animal_Run(self) (self)->run((Animal*)self)

Dog actually has a run member, so we don't have to cast the left-hand
side to avoid the compile-time error.  However, we're going to get
warnings about the right-hand side no matter what:

   * We can't make "self" a Dog*, because the Animal_run()
     function expects an Animal* as its first argument and
     if we give it a Dog* the compiler will complain.
   * We can't make "self" an Animal*, because the Dog_bark()
     function expects a Dog* as its first argument and
     if we give it an Animal* the compiler will complain.

The only way we can make these macrofied method calls work AND get
decent type-checking, I've concluded, is...

   1. Use KinoSearch-style inheritance.  (I'd rather use Ferret-style,
      but I don't see a way to make it work with type-checking)
   2. Generate a macro for EVERY method a class has, whether
      it's inherited or not.

This works:

     #define Dog_Run(self) (self)->run((Animal*)self)

     void
     Dog_chase_cat(Dog *self)
     {
         Dog_Run(self);
         Dog_Bark(self);
     }

... and we get decent type safety!

Only one problem left.  We have to generate a boatload of macros.

The solution is a code generator.  I've got one in the works, but
that'll wait for another email.

I need to think a little more about this but a code generator seems
like the best way to go so +1.

--
Dave Balmain
http://www.davebalmain.com/

Reply via email to