Invoking an overridden base method in an object

jaynabonne
30 Sept 2012, 12:14
(Note: This is a fairly obscure topic, but it may be relevant to some. Just in case, I'm posting here.)

Let's say you have a nice base type with some default scripts, and there is code somewhere that invokes those scripts by name. They provide a general object interface. As a trivial example:

<type name="MyBaseType>
<onInit type="script">
msg("My base has been init'd!")
</onInit>
</type>

And you have a derived object with that script overridden:

<object name="MyObject">
<inherit name="MyBaseType"/>
<onInit type="script">
msg("My object has been init'd")
</onInit>
</object>

And the object is invoked somewhere:

do (MyObject, "onInit")

Often when making object hierarchies, you would like to be able to invoke the base type's method from within the derived object/type's method (in a real world example, they would do more than just print messages - they could init the object's state, provide base functionality that is common to derived objects or types, etc). However, there is no standard way in Quest to do that.

There are two problems. First, you can't access a type at all. You can't access attributes or manipulate it in any way (that I have seen). They don't exist anywhere in an accessible form. The other problem is that you can't "do" an arbitrary method. "do" requires the script be an attribute on the object. You can "invoke" a script, but then there is no "this" pointer. (Perhaps you could fake it by creating a parameters dictionary and adding in your own "this" parameter.)

The following function attempts to implement invoking a base type's method. In order to solve the above problems, it does this: it requires you to create a "prototype" object following the naming convention "basetype_prototype". You can actually call it whatever you want as long as you pass that base name in, but it will keep the code sane if you try to keep the names consistent. When invoked, it creates a new attribute on the target object to hold the base script (named appropriately) and then "does" it.

The new function is this:

<function name="CallBaseMethod" parameters="o, type, method">
attribute = "callbase_" + type + "_" + method
if (not HasScript(o, attribute)) {
set (o, attribute, GetAttribute(GetObject(type + "_prototype"), method))
}
do (o, attribute)
</function>

In the above example, you would need to add an empty prototype so that the scripts can be accessed:

<object name="MyBaseType_prototype">
<inherit name="MyBaseType/>
</object>

But once that is done, you can then do:

<object name="MyObject">
<inherit name="MyBaseType"/>
<onInit type="script">
msg("My object has been init'd")
CallBaseMethod(this, "MyBaseType", "onInit")
</onInit>
</object>

and it will print out both "My object has been init'd" and "My base has been init'd!"

In case that is useful to anyone... :)

(It is left as an exercise for the reader if you want a variant that takes and passes on parameters.)

((It is also left to ponder how this could be abused. Instead of a base type, how about a prototype object that is a storehouse for object scripts, usable anywhere? The possibilities with Quest are endless.))