wp*
and wpcls*
methods.
In order to avoid too much
confusion, the methods which XWorkplace adds do not carry
the usual wp*
and
wpcls*
prefixes, but
xwp*
and xwpcls*
instead.
Only "real" XWorkplace SOM methods have this prefix; "normal" functions have other
prefixes (see the previous page for a list).
As XWorkplace became more complex over time, I have delved quite deeply into SOM. While I still don't fully understand what is going on in the SOM kernel (from what I hear, I guess nobody does) and some bugs in there keep puzzling me, I've found out some interesting stuff.
Note: The following is not required to write WPS classes. This is additional information for those who have written WPS classes already and might be interested in some SOM internals.
Most of this is related to the "WPS Classes" object
(xclslist.c
).
The SOM logic for this is in
classlst.c
in clsWPSClasses2Cnr
,
which can analyze the current SOM runtime environment (which is that
of the WPS), i.e. all the classes that have been loaded and query their
inheritance hierarchy at runtime.
This inserts the WPS class tree as recordcores into a given cnr control. This works with any containers in tree views, so that some XWorkplace dialogs can use this too. Check the sources, there's some interesting stuff.
Note that there are quite a number of functions in XWorkplace which take
SOM (WPS) objects as a parameter, even though they are not SOM methods.
See src\shared\wpsh.c
for examples. The reason for this is that
resolving SOM methods takes quite a bit of time, and calling regular
functions will work just as well, but faster.
If you look into the .IH
header files created by the SOM
compiler, you see that the C bindings for method calls are really all
macros #define'd in the header files which translate into more complex
C code.
Here's an example: if you call
_wpQueryStyle(somSelf)
in xfldr.c
, where this
method has not been overridden, the WPObject version of this method should
get called. Here's the #define in xfldr.ih
for this:
#define XFolder_wpQueryStyle WPObject_wpQueryStyle
And WPObject_wpQueryStyle
is #define'd in wpobject.h
from
the toolkit headers as follows:
#define WPObject_wpQueryStyle(somSelf) \ (SOM_Resolve(somSelf, WPObject, wpQueryStyle) \ (somSelf))Actually, there are more macros related to this, but this is the important one.
SOM_Resolve
in turn is a macro #define'd in somcdev.h
,
which calls the SOM kernel function somResolve
. That function
finally goes through the class method tables to find the actual function
address and call it.
As as result, not only does compiling of SOM code take ages (because of all the nested macros), but also calling SOM methods does, because as opposed to "static" OO languages such as C++, method resolution is occuring at run-time. IBM says in the SOM docs that calling a SOM method takes at least three times as long as calling a normal C function.
Since there is no real need to write functions as SOM methods, except when you want to design a method which can be overriden in subclasses, I have only done so in rare occasions to speed up processing.
Here are some other SOM tricks and functions which are not mentioned directly in the WPS reference (but only in the chaotic SOM docs), but these are very useful. Some of this is pretty basic, some might be new to WPS programmers, so I'll list this here:
For example, if you write a subclass of WPDataFile and need some
program-object method call (e.g. _wpQueryProgDetails
), you need
to put #include <wppgm.h>
on top of your code, or the
method binding will not be found.
This is not neccessary if the method is defined for a superclass of your
class, because the SOM headers automatically #include all the parent classes.
That is, for example,
you don't need to #include wpfsys.h
(for WPFileSystem)
for your WPDataFile subclass.
That is, for example, any Desktop object is an instance of WPObject (really one of its subclasses). The WPObject class in turn is an instance of its metaclass, M_WPObject.
In SOM, the default metaclass for a class is SOMClass. However, the WPS
overrides this behavior to create a unique metaclass for each WPS class,
which is prefixed with M_
. Since the metaclass is a class as well,
it has methods too (the so-called "class methods", which operate on class
objects, as opposed to the "instance methods" of the class itself, which operate
on instances of the class -- the Desktop objects).
_WPObject
. This is useful for calling
class methods, which always need to be invoked on a class object. So, to call
a folder class method, you do something like
_wpclsQueryOpenFolders(_WPFolder)
.
Note: If a class has been replaced, the macro will not return the original, but the replacement class. That is, if XWorkplace is installed, the above example would actually return the XFldObject class object (see notes below), and methods invoked on the WPObject class object would actually be resolved for XFldObject. That's how class replacements work in the first place. See the notes below for more.
BOOL _somIsA(pObject, pClassObject)
checks for whether
pObject is an instance of the class specified by pClassObject or one of
its subclasses.
This is extensively used in statbars.c
to
adjust status bar display depending on the class of a single object which
is currently selected.
Example: _somIsA(somSelf, _WPObject)
should always be true within
the WPS context, since all WPS classes are subclasses of WPObject.
By contrast, _somIsA(somSelf, _WPFolder)
would only return TRUE if somSelf
is a WPS folder. (This would work with _XFolder
too, but this would
require that you have access to the XFolder header files and that XFolder replaces
WPFolder. _WPFolder
always works, even if WPFolder is replaced,
because _WPFolder
would be resolved to point to the replacement class object
then -- e.g. _XFolder
.)
PSZ _somGetName(pClassObject)
returns the
class name specified by pClassObject, as specified in the IDL file.
(That is different from wpclsQueryTitle
.)
Example: _somGetName(_XFldObject)
will return "XFldObject".
SOMClass* _somGetParent(pClassObject)
returns a
parent class.
Examples: _somGetParent(_WPProgram)
returns the WPAbstract
class object (_WPAbstract
).
_somGetParent(_XFolder)
should return _WPFolder
,
unless there's some other WPFolder replacement class above XWorkplace in the
replacement list.
BOOL _somDescendedFrom(pClass, pClassParent)
returns TRUE
if pClass is descended from pClassParent.
Examples: _somDescendedFrom(_WPFolder, _WPObject)
should return
TRUE. _somDescendedFrom(_WPFolder, _WPProgram)
would return FALSE.
somResolveByName(pClassObject, "method")
gives you
a function pointer as implemented by the specified class. You should assign
this to a function pointer variable which matches the function prototype,
or otherwise you'll get crashes. This can be useful if you invoke a SOM method
frequently, for example in a loop on many objects, because then SOM method resolution
has to be done only once. If you used the macros, resolution would take place
for each iteration in the loop.
somSubstituteClass
, which appears to be doing exactly
this job.
From what I've seen, class replacements seem to work this way: Any time a class object is queried, the class manager does not return the class object of the specified class, but the object of the replacement class instead. As a result, all the method calls are not resolved for the original class, but for the replacement class instead.
Examples: If XFolder replaces WPFolder,
wpModifyPopupMenu
is not called for WPFolder,
but for XFolder instead, even though the WPS had originally called it for a folder object.
By contrast, for wpQueryIcon
, which is not overridden by XFolder, method
resolution leads to the method as implemented by the parent class, which should
be WPFolder, unless some other class replacements is present.
The class replacement mechanism
is most obvious with the class object macros described above: if
you have a _WPFolder
in your code, this returns the class object
of XFolder, i.e. _WPFolder == _XWorkplace
. So if you absolutely need
the WPFolder class object (which is normally not necessary, since this would
circumvent XFolder's functionality), you use _WPFolder
(which
returns the XFolder class object) and then
climb up the class object's inheritance tree using
_somGetParent
until _somGetName
returns "WPFolder".
somId somidThis = somIdFromString("WPObject"); SOMClass *pClassObject = _somFindClass(SOMClassMgrObject, somidThis, 0, 0);Note again that
_somFindClass
will return replacement classes
instead of the originals. (That's how the class object macros work, BTW.)
__get_somRegisteredClasses(SOMClassMgrObject)
returns a
SOMClassSequence
of all currently registered class objects. This
is used by the "WPS classes" page in the "Workplace Shell" object. See
classlst.c
for details.