The user can create metaclass objects that behave like new LYMB classes. These metaclass instances are called dynamic classes to distinguish them from the compiled classes that are found in the LYMB class library.
Dynamic classes are useful for rapid development or prototyping efforts where C programming and rebuilding of LYMB are undesirable. They are also useful in production applications, to create simple application-specific classes that need not be added to the LYMB class library.
The method functions of a dynamic class are written in the LYMB script language, which has the advantages of flexibility and rapid incremental development afforded by an interpreted language.
A dynamic classes may inherit from either a compiled class or another dynamic class, allowing the user to specialize the behavior of any LYMB class without traditional programming.
When an instance of a metaclass is created it will call an instance_init! method if one exists. Likewise when an instance of a metaclass is freed it will call the instance_free! method if one exists. These are both instance methods.
LYMB scripts to define dynamic classes may be generated by OMTool, a CASE tool for object-oriented design using a graphical notation. Use of OMTool results in an easily- understood design diagram as well as the code that defines the new classes.
The metaclass / metaobject also supports motif callbacks using the register and dispatch methods.
superclass of a dynamic class may be either a compiled or a dynamic class. If superclass is left undefined, the class is considered to be at the top of a class hierarchy. The instances created from the dynamic class will inherit from LYMB's "object" class (see metaobject(Prog_Tools)). Subclasses of compiled classes are only partially implimented, be careful!
new! creates a new dynamic class, and makes it the receiver of any following messages. (This method is not generally used by application developers.)
superclass= classname makes the named class the superclass of the receiver. The superclass may be either a dynamic class or a compiled LYMB class.
superclass? returns the name of the superclass of the receiver.
fields= (`classname', `fieldname', ... ) Defines the fields (i.e. attributes) of the the dynamic class (clearing any previously-defined fields). Classname may be the name of any LYMB compiled class or dynamic class. Within the methods of the metaclass instance variables are referenced by using the field# message implemented by metaobject. See example below.
fields+ (`classname', `fieldname', ... ) Like "fields=", but adds the specified fields to the the dynamic class without clearing previously- defined fields.
methods= (`message', ... ) Establishes the method dictionary of the dynamic class by setting its methods and corresponding messages used to invoke them. An anonymous procedure will be created with message as its name. The first argument of a method procedure must be "self", a name that refers to the object receiving the message. Additional arguments are optional. The message is a name used to invoke the method. See example below.
methods+ (`message', ... ) Like "methods=", but adds new methods to a dynamic class without clearing any previously-defined methods.
class_fields= (`classname', `fieldname', ... ) Defines the class fields (i.e. class variables) of the the dynamic class. A class variable is similar to an instance variable except that its value is shared by all instances of that dynamic class. Within the methods of the metaclass instance variables are referenced by using the field# message implemented by metaclass.
class_fields+ (`classname', `fieldname', ... ) Like "class_fields=", but adds new class fields to the the dynamic class without clearing any previously-defined class fields.
field#`fieldname' References the field (or class variable) named "fieldname". All messages remaining messages until a semicolon is seen will be directed to the class variable object.
method#`methodname' References the message (or class method) named "methodname". All remaining messages until a semicolon is seen will be directed to the arg_procedure object for that message.
class_methods= (`message, ... ) Defines the class methods of the dynamic class, and the corresponding class messages used to invoke them. Class methods are like instance methods, except that they are invoked by sending a message to the dynamic class rather than to an instance.
class_methods+ (`message, ... ) Like "class_methods=", but adds new class methods to a dynamic class without clearing previously- defined class methods.
dispatch? Meta objects use a callback dispatcher to invoke callback from motif widgets. The dispatch? method returns the name of the dispatcher. For example the activate_action of a push button would be set to [self dispatch?] where self is the metaobject that ownes the callback and pushbutton widget. You still need to register the callback procedure see register+
register+(wiget,callback_name,method_to_invoke) After you have set a widget's callback to use the dispatcher you must register the method you want to invoke. This is done using the register+ command. The first argument is the name of the widget, the second is the name of the widget's callback (i.e. "activate_action") the third is the name of the method to invoke. This should be a method of the metaclass. (i.e. for a renderer it could be "render!")
metaclass new: `node'
fields+ (`collection', `successors') fields+ (`collection', `predecessors')
methods+ (`predecessors+') methods+ (`successors+') methods+ (`predecessors?') methods+ (`successors?') ;
/* Methods for node class. */
node method# `predecessors+' /* arguments: new_pred */ call_actions= ` string new: new_pred = [args @1 string_args?];
self field# "predecessors" members+ ( new_pred ); object#new_pred field# "successors" members+ ( [self name?] ); ' ;
node method# `successors+' /* arguments: new_succ */ call_actions= ` string new: new_succ = [args @1 string_args?];
self field# "successors" members+ ( new_succ ); object#new_succ field# "predecessors" members+ ( [self name?] ); ' ;
node method# `successors?' call_actions= ` self method# `successors?' return_value= [self field# "successors" members?]; ' ;
node method# `predecessors?' call_actions= ` self print:name; self method# `predecessors?' return_value= [self field# "predecessors" members?]; ' ;
! Create a graph of 7 nodes
node new:"t1"; node new:"t2"; node new:"t3"; node new:"t4"; node new:"t5"; node new:"t6"; node new:"t7";
! Establish connection of nodes
t1 successors+( "t2" ); -- t2----t5 t1 successors+( "t3" ); -- / \ / \ t2 successors+( "t4" ); -- t1 t4 t7 t2 successors+( "t5" ); -- \ / \ / t3 successors+( "t4" ); -- t3----t6 t4 successors+( "t5" ); -- . t5 successors+( "t7" ); t6 successors+( "t7" ); t3 successors+( "t6" ); t4 successors+( "t6" );
! The node network now looks like this: ! ! t2----t5 ! / \ / \ ! t1 t4 t7 ! \ / \ / ! t3----t6 ! ! for example, sending t4 will list its predecessors and ! successors if you send print! to it:
t4 print!;