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.

Marvin Humphrey
Rectangular Research
http://www.rectangular.com/


Reply via email to