Re: LONG: Safe Character extraction.

From: Daniel A. Koepke (dkoepke@california.com)
Date: 06/29/00


On Thu, 29 Jun 2000, Patrick Dughi wrote:

> Basically, a pair of set/accessor functions for each character part,
> based on idnum lookup?  Sounds a bit annoying.

In non-OO languages that don't support exception-handling?  Very annoying.
In OO languages that do support exception-handling?  Probably not that
much better.  Compare the following in C,

    /*
     * C: get_char_gender() looks up the pointer through some means
     *    and returns it gender -- or -1 if we have an error.
     */

    byte sex = get_char_gender (cid);
    if (sex == -1) return;
    sprintf (buf, "It's a %s.\r\n", HIMHER (sex));
    send_to_char (buf, to);


    // C++: A CharacterID class is at work, here.  It has a private(?)
    //      function named character() inside of it that fetches the
    //      character pointer.  If it fails to fetch the pointer, it
    //      throws a BadCharacter exception.  All of its accessors use
    //      the character() function, so they all throw.

    try
    {
        to.writeLine ("It's a %s.", HIMHER (cid.gender()));
    }
    catch (BadCharacter* bc)
    {
        delete bc; // ?
        return;
    }

In this minimal example, the C code seems much better.  However, the
exception handling mechanism permits us to only handle an error once, not
every time it might occur:

    try
    {
        for (int hits = 0; hits < numHits; hits++)
        {
            .
            .  // Calculate the damage amount
            .
            fighting ().character ()->damage (this, amount);
        }
    }
    catch (BadCharacter* cd)
    {
        fighting (NOBODY);
        writeLine ("Congratulations, you won!");
    }

It's still really ugly, but imagine having to write a pointer-free version
of act() using the C mechanism demonstrated above.  The error checking in
each case...break block would be more than enough to drive me completely
mad.  OTOH, both versions could get away with,

    struct character_data *ch = get_char_by_id (cid);
    .
    .
    .

if the code is guaranteed to not invalidate the pointer at any time during
the scope of 'ch'.

> Perl-like hash tables?

In my Python code I use the builtin dictionary type and a unique string
identifier for each object.  The id is then stored, rather than references
to another object, and when it's needed we do (pseudo-codish),

    cd = character.charDictionary  # Alias

    class EarlyDetectionSystem(Object):
        def tripped(self, tripped):     # Setup as ON_ENTER listener.
            if cd.has_key(self.owner):
                owner = cd[self.owner]
                report_to = owner
            else:
                report_to[:] = everyone[:]
                report_to.remove(tripped)

            report_to.message( \
               tripped.name + " tripped the EDS at " + \
               tripped.location.name + "!")

            # expire after N uses
            self.uses = self.uses - 1

            if self.uses < 1:
                self.fromloc()
                del self
        # end tripped
    # end EarlyDetectionSystem

Objects and instances have different ids.  Basically, we define a factory
function that creates a new object instance.  It takes its "prototypes"
unique id and munges it.  So if the id was, "eds," the first instance
would be, "eds__instance_0," the second instance, "eds__instance_1," etc.

> I'll see if I can't write a general way using a sort of named lookup
> thing... get_value(chidnum,"gender"); or something..

That will require (potentially excessive) casting.

    byte sex = *((int *) get_value(chidnum, "gender"));

and will potentially be slow, depending upon how you implement the lookup
for data elements within the character.  At that point, it'd almost be
better to use a complete generic attribute system stored on the character.
The lookup wouldn't be that much more expensive and the flexibility of not
having a type system for your wajas (world whatcha-ma-jiggers; i.e., an
item, room, character, whatever) could be interesting (but allow nothing
that couldn't be faked and be just as playable as far as the player is
concerned).

As an aside, Microsoft's C# (which, surprisingly, they actually submitted
to the ECMA standards board) language provides property get/set methods
that aid in rapid application development (e.g., "code evolution") that
could find interesting applications, here:

    public class CharacterId
    {
        private long _cid;

        public string Name {
            get { // as in, writeLine (cid.Name);
                Character obj = FindPointer (_cid);

                if (obj == null)
                    throw new Exception ("Bad character.");

                return obj.Name;
            }

            set { // as in, cid.Name = "";
                Character obj = FindPointer (_cid);

                if (obj == null)
                    throw new Exception ("Bad character.");

                obj.Name = value;
            }
        }

        .
        .
        .
    }

In this case, you would treat CharacterId precisely as you would the
Character class.  The underlying, invisible property code would handle the
differences in a user-transparent manner.  (This is both a good and bad
thing.)


-dak : Please cite your quotations properly.  This makes it much easier to
       keep track of the thread if you miss a post, etc.


     +------------------------------------------------------------+
     | Ensure that you have read the CircleMUD Mailing List FAQ:  |
     |  http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html  |
     +------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 04/10/01 PDT