Node: Accessing Type Meta-Objects, Next: Accessing Subtypes, Previous: Defining New Subtypes, Up: Subtypes
As mentioned previously all Lpp objects are of some type in the Lpp
type hierarchy where each type has a Type meta-object
associated with it. We call these meta objects because they
contain the information about and type functionality of the ordinary
Lpp objects they are associated with. There is one type meta-object
for all ordinary Lpp object instances of that type.
These Type objects are first class Lpp objects and can be
manipulated and accessed dynamically. For example the print methods
of specific types can be accessed and set dynamically. First we show
two functions for getting the type meta-object itself.
| typeOf object | Function |
This function returns returns the type meta-object for the type of the Lpp object object.
| type name | Macro |
This macro returns the type meta-object named name. Here are some examples:
let typeList =
list(type(Integer), type(Symbol), type(MyType));
eq(typeOf(L(23)), first(typeList)) => t
eq(typeOf(S(23)), second(typeList)) => t
| typeName object | Function |
| typeNameL object | Function |
typeName returns a char* representing the type print
name of the object object. For example:
let object = S(red);
typeName(object) => "Symbol"
The typeNameL function does the same but returns an Lpp String
object.
A Type meta-object contains various functions that dispatch
on the type of a given object dynamically. These are similar to
virtual functions but are more powerful in that they can be
manipulated dynamically. Lpp provides some built in type dispatching
functions for things such as printing and equality of objects.
All Lpp Type meta-objects when created have default dispatching
functions generated. The user however may access these dispatching
functions or set them to new dispatching functions dynamically. In
general there are slots in the Type meta-object that can be
accessed by functions of the form
setTypeXXXX type data
getTypeXXXX type
where XXXX refers to the kind of data being accessed in the
given type where type is an Lpp Type meta-object. And
setTypeXXXX sets the data and getTypeXXXX returns
the data data last set. When setting data is given as
nil it usually gets interpreted as setting back to the a
default. This setting and getting can be done dynamically at run
time. Here are some examples
let original = getTypePrinc(type(MyType));
setTypePrinc(type(MyType), L(MyTypePrincFunction));
setTypePrinc(type(MyType), Nil);
setTypePrinc(type(MyType), original);
The first line sets original to the current princ
printer function of MyType. The second line sets the
princ printer function of MyType to
MyTypePrincFunction. The third line would set the princ
printer function to the Lpp default. And the last line sets the
princ printer function back to what it was originally.
| setTypePrinc type function | Function |
| getTypePrinc type | Function |
| setTypePrin1 type function | Function |
| getTypePrin1 type | Function |
All Lpp objects have princ and prin1 methods used by the
Lpp printing family of functions and C++ stream IO of Lpp objects,
See Input Output. These functions allow the user to set or get the
print method of a given type where type is a Type
meta-object. The functions setTypePrinc and
setTypePrin1 sets the Lpp function object for the princ
and prin1 methods respectively. The function argument
is a function taking two arguments, first an object of type let
and second a stream of type ostream&. The function
should return the first argument object as both princ and
prin1 do. The functions getTypePrinc and
getTypePrin1 returns the last function set.
For example suppose that we had defined an Lpp class called
MyClass that has two slots with accessors getSlot1 and
getSlot2. Then the following code would set up a princ
type printer for MyClass
let princMyClass(let obj, ostream& s) {
s << "MyClass object: slot1: "
<< getSlot1(obj) << " slot2: " << getSlot2(obj);
return obj;}
let mc1 = makeInstance(MyClass(76, "trombones"));
cout << mc1 << endl; // Would produce
<Lpp Testclass>
setTypePrinc(type(MyClass), L(princMyClass)); // Then
cout << mc1 << endl; // Would produce instead
MyClass object: slot1: 76 slot2: trombones
The default princ and prin1 type dispatching functions
for any Lpp type will print an object as
<Lpp xxxx>
where xxxx will be the type name of the object. The
princ type dispatching function is the one used by the
princ function and also by the << operator as seen
above.
| setTypePrins type function | Function |
setTypePrins sets both the princ and prn1
printing function to the same function.
When an Lpp equality predicate of either equal or equalp
is called, an equality dispatcher function of the Type
meta-object is called to compute the predicate. The two arguments of
the equality predicate are passed to the dispatcher function and the
returned result is the result of the equality predicate. The equality
dispatcher functions can be accessed with the following
| setTypeEqual type function | Function |
| getTypeEqual type | Function |
| setTypeEqualp type function | Function |
| getTypeEqualp type | Function |
The setTypeEqual function sets the equal dispatcher function of
the Type meta-object type to the given function
function which is an Lpp function object of two arguments.
When equal is called given two objects where the first object
is of this type then the function function is automatically
dispatched on the object. Given both objects the function should
return t if the objects are considered to be "equal" and
nil otherwise. As an example suppose that we had defined an
Lpp class called MyClass then
let myClassEqual(let o1, let o2) {
return equal(the(MyClass, o1).slot1, the(MyClass, o2).slot1);}
let mc1 = makeInstance(Myclass("foo"));
let mc2 = makeInstance(Myclass("foo"));
equal(mc1, mc2) => nil
setTypeEqual(type(Myclass), L(myClassEqual));
equal(mc1, mc2) => t
setTypeEqualp does the same thing as setTypeEqual except
that when equalp is called given two objects where the first
object is of the type type then the function function is
automatically dispatched on the object. Given both objects the
function should return t if the objects are considered to be
"equalp" and nil otherwise. getTypeEqual and
getTypeEqualp will return the last equal, equalp
dispatcher functions respectively set for the given type.
If a type meta-object does not have a equality dispatcher function set
this way then eq will be used as the default.
| setTypeExt type extension | Function |
| getTypeExt type | Function |
The Lpp user can set up his own Type meta-object extension to any Lpp
Type meta-object. He first defines an Lpp class that will be the
meta-object extension class and then instantiates a object of that
class which he then sets in the desired Type meta-object by calling
setTypeExt on the desired Type type and meta-object
extension is that newly instantiated object. getTypeExt
would return the last extension set in the given type
meta-object.
As an example this is useful when over some number of classes the user
wants to dispatch on a common action, like the princ printing
action above. Since the extension is a first class Lpp object it can
be exchanged dynamically. So, for example, the user can cause a
collection of classes to behave one way in one mode and then another
way in a different mode. Also all Lpp objects Type meta-objects are
guaranteed to have an extension even if only nil which would
usually be a default case or no action.