Phantasmal MUD Lib for DGD

Phantasmal Site > DGD > DGD LPC Reference > Create Funcs

Create() Functions in DGD

From: dgd at list.imaginary.com (Noah Gibbs)
Date: Mon Oct 20 14:18:02 2003
Subject: [DGD] Some basics

--- Nihilxaos wrote:
> 1) When an object is compiled it's create is
> called.

  Nope, but close.  The first time a function is
called on the object, create() is called first.  So if
you've just made an object called "bob" and want to
initialize it, you can say "bob->NoSuchFunction()",
which will return nil, and will do nothing but call
create(), at least if the function doesn't exist.

  I think create() also gets called if you clone from
it, but I'm not 100% sure.  This is all written down
somewhere :-)

> Thus if I write a log 
> daemon logd.c (aliased by LOGD) and pull the usual
> if (!find_object(LOGD)) 
> {compile_object(LOGD);} it sees if the logd object
> has been loaded into memory, 
> and if not it compiles it and then runs create since
> you obviously want to 
> create it. This right?

  Almost.  Create isn't called yet.

> It seems a little off to me,
> but that seems to be how 
> things are working. Or is it more correct to say
> that create is called once the 
> first call is made to one of the object's functions?

  Yup.

> Basically this is going on the assumption
> that inheriting brings the 
> inherited data types into the object, but keeps any
> non-overridden code in a 
> separate object so it can be called from any of its
> children.

  I'm not sure what you mean here.  Inheriting from an
object doesn't change that object, so yes, the code
that you override in a child still exists in the
parent.  Is that what you're getting at?  And
functions that you don't override are callable from
the child, but on the child's copy of the data.  In
the Kernel MUDLib it goes one step further -- a
library NEVER has its create() function called.  That
means its data never gets instantiated, which is a
*good* thing -- it's the key to being able to upgrade
in place.

> Thus when you 
> destruct an object it in turn destructs its
> ancestors,

  You can make it do that.  It doesn't happen
automatically, at least not in that way.  But the
ObjectD could do that for you if you wanted.

> not to ultimately wipe 
> out the ancestor such that it can't be called by
> other like objects, but so it 
> removes the links between it and its ancestors.
> (that make sense?)

  I think you mean the right thing.  Yes, it destructs
its ancestors...  But even if they're destructed, they
don't *really* go away until nobody references them
(inherits from them) any more.  That's why the ObjectD
keeps a big list of destructed objects around.  Those
are libraries that somebody inherits from but that
have been destructed already.  When the last child of
that library is recompiled (pointing at the new,
non-destructed copy), the old destructed version goes
away because nobody references it any more.

From: dgd at list.imaginary.com (Stephen Schmidt)
Date: Mon Oct 20 14:23:00 2003
Subject: [DGD] Some basics

On Mon, 20 Oct 2003 Nihilxaos wrote:
> 1) When an object is compiled it's create is called.

This is not quite correct, as you've already deduced. Two things
are true. First, as you wrote:

> Or is it more correct to say that create is called once the
> first call is made to one of the object's functions?

Yes, create isn't run until it has to run, that is, when
some other object wants to call into this one. Until then,
this object is just sitting in The Void by itself doing
no harm to anyone, so no need to run create() in it.

The second caveat is that if you are recompiling the code of
an existing object, create() will not be called in that case,
even when a function is called, because presumably the object
(which was not destructed) has some internal state that should
be preserved. The object hasn't been recreated, just had its
code recompiled. Whatever its variable values were, those
should not change. Normally calling create() sets variable
values to some default initial values, and you wouldn't want
to do that if all you were doing was changing the code.

In Melville, the update command has a flag (I don't remember
exactly what, I think -c) to force calling the create function.
So "update foo.c" updates the code but doesn't call create,
while "update -c foo.c" does both. (I may have set it up so
that there's also a flag that triggers destruction of the
original object. You would want to call create() explicitly
in that case too, probably.)

This is, of course, a change in behavior from LPMud, and I
think it's fair to say that I have gotten more bug reports
from Melville caused by this issue (people not understanding
why create() wasn't being called when they updated an object)
than any other single source.

> 2) When I inherit something, if it doesn't already exist I have to call it's
> create function.

I think not. create() should get called by the time you use the
object for anything else. The only time you'd need to invoke
create() yourself is when you have a logging line or something
that you want invoked right when the object is created, instead
of waiting until the object is first used. Of course, I'm guessing
that is exactly the case here :)

> Thus you'll see something like:
> inherit foon FOON;
> [...]
> foon::create();
> Is that because we want to make sure that FOON has been loaded into
> memory, or do I have to call create() to make sure that the inherited
> object is properly configured for this object and not for something
> that may have inherited it before?

I'm not sure I understand this correctly, but if I do, then this
is for configuration. You probably want to do this in the form:

inherit foon FOON;

void create()
  foon::create();
}

so that foon's create runs when this object's create does. Anything
else could result in bizzare behavior. For example, say foon is
container.c, and it tracks where the container is open or closed,
and create() in foon initializes all newly-created containers to
be closed. If you invoked foon::create() from anywhere other
than the inheriting object (say, treasure_chest.c)'s create()
function, then the chest would snap shut when create() in foon
was called. Be a pity if the key was inside, wouldn't it?  :)

> Basically this is going on the assumption that inheriting brings the
> inherited data types into the object, but keeps any non-overridden code in a
> separate object so it can be called from any of its children.

That, I think, is correct, and actually the overridden code is
there too. But I'm not totally sure of this.

> Thus when you
> destruct an object it in turn destructs its ancestors, not to ultimately wipe
> out the ancestor such that it can't be called by other like objects, but so it
> removes the links between it and its ancestors. (that make sense?)

This is where garbage collection comes in. When 10 objects inherit
foon.c, and of those 10 objects, 1 has 20 clones, then you can't
destruct foon.c until all 20 clones and the 10 objects are gone.
And when you recompile foon.c so you can change behavior in one of
those 10 objects, do you keep the old behavior in the other 9 objects,
or not?

This is an area where I understand the problems but not the exact
solutions, so I'm going to turn discussion over to the driver gurus
at this point.

Steve

From: dgd at list.imaginary.com (Erwin Harte)
Date: Mon Oct 20 14:27:01 2003
Subject: [DGD] Re: Some basics

On Mon, Oct 20, 2003 at 12:17:33PM -0700, Noah Gibbs wrote:
> --- Nihilxaos wrote:
> > 1) When an object is compiled it's create is
> > called.
> 
>   Nope, but close.  The first time a function is
> called on the object, create() is called first.  So if
> you've just made an object called "bob" and want to
> initialize it, you can say "bob->NoSuchFunction()",
> which will return nil, and will do nothing but call
> create(), at least if the function doesn't exist.

I'd go for this instead:

    call_other(bob, "")

Less chance of NoSuchFunction() accidentally (or intentionally, if
someone is trying to break things) existing.

[...]
> > Basically this is going on the assumption
> > that inheriting brings the 
> > inherited data types into the object, but keeps any
> > non-overridden code in a 
> > separate object so it can be called from any of its
> > children.

If with 'data types' you mean 'non-private variables', then yes.

Erwin.
-- 
Erwin Harte

From: dgd at list.imaginary.com (Stephen Schmidt)
Date: Wed Mar 20 23:46:01 2002
Subject: Re[2]: [DGD] Inherits.

On Thu, 21 Mar 2002, Vladimir I. Lazarenko wrote:
> hmm. let's take my situation as an example.
> I have a thingie called channel_d (Melville mudlib).
> If i make changes to channel_d and then issue 'update channel_d.c'
> my changes are not there.
> Am I doing anything wrong?

Sorta. When an existing object is updated in DGD, create() is
not called in the object. Because, in the channel_d, the data
is loaded in create, when you update it, the data is not there.

There are several fixes:

1) Change your update command to invoke create() if the object
existed before, or at least, have a flag so the user can invoke
that behavior;

2) Change the channel daemon so that, when any call is made into
it, it will check to see if its data is loaded, and load it if not;

3) Create a new command, like the update command, that invokes
create() when you want to.

All of these have drawbacks.

The soul daemon has the same problem (in fact, that's usually
the one people notice first).

Steve

From: dgd at list.imaginary.com (Erwin Harte)
Date: Wed Mar 20 23:54:01 2002
Subject: [DGD] Inherits.

On Thu, Mar 21, 2002 at 12:17:05AM -0500, Stephen Schmidt wrote:
> On Thu, 21 Mar 2002, Vladimir I. Lazarenko wrote:
> > hmm. let's take my situation as an example.
> > I have a thingie called channel_d (Melville mudlib).
> > If i make changes to channel_d and then issue 'update channel_d.c'
> > my changes are not there.
> > Am I doing anything wrong?
> 
> Sorta. When an existing object is updated in DGD, create() is
> not called in the object. Because, in the channel_d, the data
> is loaded in create, when you update it, the data is not there.

I'm not sure you and I are talking about the same DGD here, then.

DGD's compile_object() never calls any functions in the object that it
returns, the create() (or _F_create() or whatever the creator function
is that you use for Melville) function is called just before someone
or something calls a function in the object for the first time.

If you're _recompiling_ an object, all you're doing is replace the
code, you don't want to go and call create() functions in there, you
could very likely damage important data in there when all you did was
fix an if-statement condition or something like that.  Perhaps you
could add something to your command so that if you're recompiling an
object instead of compiling one that didn't exist before, you call a
different function like upgraded(), from a 0-call_out (because the
new code won't be effective until after the thread finishes).

Hope that helps,

Erwin.
-- 
Erwin Harte