incubator-lucy-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "David Balmain" <>
Subject Re: OO Design -- initialization
Date Tue, 19 Dec 2006 07:06:38 GMT
On 10/31/06, Marvin Humphrey <> wrote:
> On Oct 23, 2006, at 6:44 PM, David Balmain wrote:
> >> There are costs to this approach.  It requires a fair amount of
> >> boilerplate code. (We might want to consider using a symbol
> >> generator.)  I'm also pretty sure we'll want to write some
> >> bootstrapping code to prep the virtual tables at startup, because it
> >> will be difficult if not impossible to resolve all aspects of
> >> inheritance at compile-time.
> >
> > Could you give me an example of where this is difficult?
> The place where it's hardest is in the definition statements for the
> class data.  You'd like child classes to clone the parent's table,
> then selectively overwrite the slots that they don't inherit.  That's
> possible with bootstrapping, but I can't think of a way to do that at
> compile time.
> The only way to make compile-time resolution work is to copy-and-
> paste each member that gets inherited.  The bigger the parent class,
> the more copying and pasting.  And if we ever add a method to Obj,
> from which all classes inherit, we have to manually add that member
> to every class.
> Here's how compile-time resolution with Ferret-style inheritance looks:
>      /* in Dog.h */
>      typedef struct DOG {
>          Animal super;
>          Dog_chase_cats_t chase_cats;
>      } DOG;
>      extern const DOG DOG_CLASSDATA;
>      /* in Animal/Dog.c */
>      const DOG DOG_CLASSDATA = {
>          {
>              "Animal::Dog",
>              (Animal_speak_t) Dog_speak,
>              (Animal_eat_t)   Dog_eat
>          },
>          Dog_chase_cats
>      };
>      /* in Animal/Dog/PitBull.c */
>          {
>              {
>                  "Animal::PitBull",
>                  (Animal_speak_t) Dog_speak,
>                  (Animal_eat_t)   Dog_eat
>              },
>              Dog_chase_cats
>          },
>          PitBull_chase_humans
>      };

Actually, I like this more than the code below. To me the braces help,
not hinder. But to each his own I guess. Also, we could macrofy this
if wanted to;

        #define DOG_DATA(name) {\
                (Animal_speak_t) Dog_speak,\
                (Animal_eat_t)   Dog_eat\

        /* in Animal/Dog.c */
        const DOG DOG_CLASSDATA = {

        /* in Animal/Dog/PitBull.c */

> That last one's getting pretty unwieldy, and we've only got a couple
> members -- imagine what things start to look like with MultiReader.
> We can't macrofy these, either, because functions get overridden
> right in the middle of the stuff we'd like to macrofy.

Not sure what you mean by not being able to macrofy these. I guess
I've missed something.

> <snip>marginally cleaner KinoSearch example</snip>
> With bootstrapping, you can do this instead:
>      /* in Animal/Dog/PitBull.c */
>      void
>      PitBull_bootstrap()
>      {
>          memcpy(&PITBULL_CLASSDATA, &DOG_CLASSDATA, sizeof(DOG));
>          PITBULL_CLASSDATA.chase_humans = PitBull_chase_humans;
>      }
> That's a lot simpler.
> > I really
> > don't like the idea of bootstrapping, simply because it makes it
> > impossible to clean up the memory allocated during this process when
> > the application exits which in turn makes it difficult to use valgrind
> > to track down memory leaks.
> I certainly want to be using Valgrind for that, and I don't think I
> see a conflict there.
> If we declare the class tables as global variables, and the
> bootstrapping performs assignment but no allocation as in the above
> example, Valgrind won't conflate the globals with memory leaked from
> malloc.

With macros I don't think it makes that much difference but I'm sure
you'll be able to enlighten me as to where my macro example fails, in
which case, bootstrapping is fine with me. When you said bootstrapping
initially I was think memory allocation but if that isn't happening
then I don't have a problem with it.

> And even if we do malloc the tables, shouldn't it be possible to
> clean up everything legit using a cascade of tear-down functions?
> The trick is that you have to make sure all objects are destroyed
> before you remove the classdata they rely upon.  I think we can pull
> that off.  But I don't think we have to.

This is fine in C but I'm not sure how you'd do it from the Ruby
interpreter. I wish Ruby cleaned up it's memory allocation. That is my
major gripe with Ruby. It makes  very difficult to write substantial
extensions like Ferret.

> As an aside, I wonder if there's a potential benefit for resolving
> this stuff at compile-time in that the more that you declare as
> const, the better the compiler does at optimization.  I don't think
> there is, though, because even if all the virtual tables are declared
> as const, the compiler never knows _which_ const table it will get
> pointed at during any given method invocation.  So it's always going
> to have to read the table pointer from the object into a register,
> add the offset for that method to get at the right function pointer
> in the classdata struct, and jump into code from there.

I'd like to know whether this makes a difference too. I'm guessing the
same as you that it won't have any affect.

> > I'm happy to give up my beloved valgrind for this
> > project if it is really necessary.
> I consider Valgrind absolutely essential for development.  KinoSearch
> has a script ($DIST_ROOT/devel/valgrind_test.plx), which runs the
> whole test suite under Valgrind and logs the output.  It takes 15
> minutes instead of 9 seconds to finish, but it's worth it.

I feel the same way but Valgrind + Ruby = :(. This is one of the
things that got me thinking about building a database like search
engine which runs in a separate process to Ruby as a server, but that
is a whole other story.

Dave Balmain

View raw message