[lsdkernel-devel] LSD Status update, and Object model preview
Status: Pre-Alpha
Brought to you by:
lsdadam
|
From: Adam M. <ad...@fs...> - 2004-10-14 06:23:53
|
All,
Please note: This email contains a long tutorial on many new things in
LSD. It's a proposal/request for comment on the new mechanism. I
suggest you save time to carefully consider the entire thing. Do not
skim it, and know you've been warned about the vast size of it.
I've been hacking these past few days on a python script to act as a
temporary pre-processor for LSD kernel's Object Model. This has the
side effect of REQUIRING the build environment to HAVE python installed
(By default in /usr/bin/python -- An autoconf like device might come
sometime in the future to help automate the build process.) Since for
now, we're all a smart bunch of hackers, we should know how to tweak
things in makefiles that are broken for our systems.
To outline, for those who'll be using the current pre-processor, it
runs after the C preprocesor runs, and is far more simple minded than
the C preprocessor. (It's in effect a meta-pre-processor, as the data
generated by it, are really generated by the pre-processing phase.
These steps *COULD* be reversed, but I like it better this way.) The
preprocessor recognises two symbols: _Define and _EndDef. These brace
a definition, which may span multiple lines. The first token of the
"Definition block" is the macro-identifier token. Whenever this token
is seen elsewhere in the text, it is replaced with the remainder of the
definition block, without the _EndDef. The preprocessor (stupidly?)
blindly accepts redefinitions of symbols. This gives us interesting
and desired behaviours, for working with Objects, which is the purpose
of the macro-processor in the first place.
The Object Model of the LSD kernel is coming to fruition. It's a
semi-preprocessor based object model. Objects get defined and helped
along by the preprocessor, but may be dynamically added to the
programme (New generations of object that is!) text of the kernel,
without needing to recompile.
------------------------------------------------------------------------
------------------------------
A WARNING: FROM THIS POINT FORWARD, THIS IS A TUTORIAL ON THE
NATURE OF OBJECTS WITHIN LSD. I WOULD NOT RECOMMEND READING
THIS CASUALLY. SAVE TIME TO READ IT.
------------------------------------------------------------------------
-------------------------------
LSD has header files, and a python-based preprocessor which extend the
C language to have a few new keywords:
class, extends, implements, contains, initialise, initialiser, get_this
There are a few new "generic" objects created, and a few functions and
data considered to be in all Objects. These can be thought of to be
LIKE reserved words:
Objects: Object, K_Object, Device
functions/data: this, new(), delete(), promote(), constructor(), super,
type, size, magic_pool
The new syntax is exampled as follows:
class ( SomeNewClass extends AnOlderClass
contains
int someNewElement;
void (*someNewFunction)();
);
and must have an accompanying file, which includes the header file for
this class,
and uses the "initialiser" macro to initialise the class-object:
initialiser( SomeNewClass);
From this point, all the functions defined to have been added by
SomeNewClass, and
all inherited functions, and all new and inherited datatypes, are set
to initial values.
The initial values for variables must be set, in the initialiser macro.
A mechanism for doing this will come shortly.
All newly declared functions and inherited functions get set to local
entities.
The functions are in the form:
SomeNewClass$someFunction()
Which means that "Dollars-in-Identifiers" must be enabled.
The values for this, super, type and magic_pool for the new object are
set to appropriate (sane) values -- magic_pool, recieves the object
magic number, the type gets a copy of the string name for the type.
The this and super elements are set appropriately. Size gets the size
of a new object of this type. (this is set to point to the "class
object" for the type of the object, and super to the parent object
type. o->super must always be equal to o->type->super, in sane
circumstances.)
The as-yet unused keywords initialise and implements are still
reserved, as they will be used to extend the class syntax somehow.
(implements will likely be some kind of member function in the future.)
Initialise is being considered as an extra clause to the class
definition system, to specify any specific initialisers for your class.
The mechanism will allow class definitions derived from a class
specifying initialisers, to inherit those
initialisations, and optionally override them.
Within the Object system, there's some simple semantics, regarding
objects.
Objects have two reserved names for each Object type. The name of the
object, i.e.: Object, and the typename for the object: Object_t. Or:
Device, and Device_t. This is necessary due to constraints of the C
programming language. Any pointers (references) to objects must be of
type Object_t (or MyObject_t, or something). The "class methods and
data" for an object are stored in that object's name:
Object->promote(), or Object->size.
An example of how to declare a pointer to, and allocate memory for, a
new object of type Device:
Device_t * d = Device->new();
One could have obviously used also:
Object_t * o = Device->new();
Depending upon compile time options to the CC, you may or may not need
to cast (or get warned about it.)
The new function for each object type returns a pointer to an object of
this type. I believe this is the correct behaviour, in this
circumstance.
The delete function is NOT to be called from the class object. To do
so will remove the class object, and not allow use of the class methods
any longer. (Or could crash the kernel.)
The delete function is to be called from the object you wish to delete.
This I feel is the correct behaviour since an object knows what
cleanup it must do, and will likely need access to it's own modified
data, and modified method table.
Example of deleting the "d" instance of Device:
d->delete();
It's fairly straightforward.
All objects have access to a macro, which grants access to a "this"
pointer, to the object being worked on. This is the "get_this" macro.
get_this requires two arguments: The name of the object being worked on
(The class name), and the function name this method should be called
as, from the object (The method name, as pointed to by the class.)
E.G.:
void Object$delete()
{
get_this(Object, delete);
if(this == this->this)
{
// We have a BIG problem!
// our object is the class-instance
} else {
free(this);
}
}
Another example:
int IDE_DiscDevice$write(datap buf, int len)
{
get_this(IDE_DiscDevice, write);
//Perform necessary sanity checks.
// call various hardware routines, with bus addresses
// stored in: this.
//example:
IDE_Bus[this->IDE_BusID]->flush(this->active_block, this->device_id);
return len;
}
Since "this" is not a reserved word, you may use it as a parameter to
you function,
instead of the automagical pickup of the calling object. For some
circumstances this
would be preferrable.
In the case of passing on to a super's function, the function CANNOT be
one which
handles "this" or else we run into the problem that the super's address
becomes the "this", when using the automatic method. Passing the
object is advisable in this circumstance. Descending through the
heirarchical tree is desirable, to both propogate changes, and save
time writing code.
There are doubtless many inconsitencies in the handling of Objects at
the moment. I submit this as a work in progress. I appreciate any and
all feedback.
Please keep in mind that this mechanism is to be used in a kernel
programming enviroment, so an overly generic, or bloated system is
undesireable. The major focus for this effort is to provide LSD with a
form of "automatic code generation" and "automatic code reusage" where
it's most likely to be useful, to give us a language to define
structure-based inheritable objects, and grant us polymorphic behaviour
on those objects, for better system design.
Regards to all,
--
ADAM David Alan Martin
|