You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@lucy.apache.org by David Balmain <db...@gmail.com> on 2006/12/19 08:08:42 UTC

Re: OO Design -- type checking

On 10/31/06, Marvin Humphrey <ma...@rectangular.com> 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/

Re: OO Design -- type checking

Posted by David Balmain <db...@gmail.com>.
On 12/21/06, Marvin Humphrey <ma...@rectangular.com> wrote:
> On Dec 18, 2006, at 11:08 PM, David Balmain wrote:
>
> > I need to think a little more about this but a code generator seems
> > like the best way to go so +1.
>
> While you were away I wrote one in Perl called "boilerplater.pl" and
> integrated it into KinoSearch.  The implementation turned out to be
> somewhat more elaborate than I'd originally planned, because it
> parses all header files and builds a model of the entire object
> hierarchy before generating any code.  However, once it was mostly
> finished, all of a sudden everything else got a lot easier.  :)
>
> Note that because this is a code generator which produces
> deterministic output directly from C source code, Perl is only
> required on the developer's system (and there are no dependencies
> other than Perl 5.6).  I thought about using Ruby or Java, because I
> didn't want to appear partisan, but oh well. :)  It could be re-
> implemented if need be, though there's a lot of the kind of text
> wrangling which is Perl's strong suit.

Actually, Perl would me my preference. I think it is probably the most
ubiquitous and I have no problem with it being a requirement on the
developers machine.

> Here's some generated code, for the TopDocCollector class:
> <snip>generated code links</snip>

This looks good. Does this take away the need to bootstrap the objects
on start up? Maybe you cover this in a different email. I'll check
now. It's looking very cool so far.

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

Re: OO Design -- type checking

Posted by Marvin Humphrey <ma...@rectangular.com>.
On Dec 18, 2006, at 11:08 PM, David Balmain wrote:

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

While you were away I wrote one in Perl called "boilerplater.pl" and  
integrated it into KinoSearch.  The implementation turned out to be  
somewhat more elaborate than I'd originally planned, because it  
parses all header files and builds a model of the entire object  
hierarchy before generating any code.  However, once it was mostly  
finished, all of a sudden everything else got a lot easier.  :)

Note that because this is a code generator which produces  
deterministic output directly from C source code, Perl is only  
required on the developer's system (and there are no dependencies  
other than Perl 5.6).  I thought about using Ruby or Java, because I  
didn't want to appear partisan, but oh well. :)  It could be re- 
implemented if need be, though there's a lot of the kind of text  
wrangling which is Perl's strong suit.

Here's some generated code, for the TopDocCollector class:

   http://www.rectangular.com/svn/kinosearch/trunk/c_src/KinoSearch/ 
Search/TopDocCollector.r

The TopDocCollector.h header file which fed boilerplater.pl:

   http://www.rectangular.com/svn/kinosearch/trunk/c_src/KinoSearch/ 
Search/TopDocCollector.h

The TopDocCollector C file, which makes use of a couple HitQueue  
methods:

   http://www.rectangular.com/svn/kinosearch/trunk/c_src/KinoSearch/ 
Search/TopDocCollector.c

boilerplater.pl itself (POD documentation at the end)...

   http://www.rectangular.com/svn/kinosearch/trunk/devel/boilerplater.pl

If you change out every kino_, Kino_, KINO_ and KinoSearch for lucy_,  
Lucy, LUCY, and Lucy, that TopDocCollector code is basically what I'd  
like to see in our source repository.

Thoughts?

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