The basic XPCE type-syntax is described in section 3.2.1 of this manual. Types are first-class reusable XPCE objects that are created from the type-declaration in arguments and variables. The conversion from the textual representation to the object is performed by XPCE itself (together with the resource syntax, one of the few places where XPCE defines syntax). All types can be specified as Prolog quoted atoms. For example:
mymethod(Me, A:'graphical|dict_item|0..') :-> ...
For most cases however, this is not necessary. If the type is not an atom, the class-compiler will convert the Prolog term into an atom suitable for XPCE's type system. Hence, [point] will translate to the atom '[point]', which is interpreted by XPCE as ``an instance of class point or the constant @default''. The atoms * and ... are defined as postfix operators, while .. is an infix operator. This makes `any ...' a valid notation for ``any number of anything'' (see section 7.5.2 below) and `0..5' a valid expression for ``an integer in the range 0 to 5 (including the boundaries).
Also, [box|circle] is a valid description for ``an instance of box or circle or the constant @default. Note however that [box|circle|ellipse] is not valid Prolog syntax and should be written as '[box|circle|ellipse]'. Whenever you are in doubt, use quotes to prevent surprises.
Methods
such as `chain->
initialise'
and `string->
format'
handle an arbitrary number of arguments. The argument declaration for
such a method first defines a number (possibly zero) of `normal'
arguments. The last argument is postfixed with `...'. The
arguments assigned to the `vararg' type are passed in a Prolog list.
Below is a refinement of `label->
report'
that will colour the label depending on the nature of the message. The ->
report
method takes two obligatory arguments, the kind of the report
and a format string, as well as an undefined number of
arguments required by the format specification.
:- pce_begin_class(coloured_reporter, label, "Coloured reporter label"). report(L, Kind:name, Format:char_array, Args:any ...) :-> Msg =.. [report, Kind, Format | Args], send_super(L, Msg), get(L, colour_from_report_category, Kind, Colour), send(L, colour, Colour). colour_from_report_category(L, Kind:name, Colour:colour) :<- <left to the user>. :- pce_end_class.
XPCE provides two alternatives to multiple inheritance. Delegation is discussed in section C.4. See also the directive delegate_to/1 for user-defined class definitions. The template mechanism is much closer to real multiple inheritance. A template is a named partial class-definition that may be included in other classes. It behaves as if the source-code of the template definition was literally included at the place of the use_class_template/1 directive.
In fact, the class-attributes (variables, method objects) are copied, while the implementation (the Prolog clauses) are shared between multiple references to the same template.
Templates itself form a hierarchy below class template, which is an immediate subclass of object. Including a template will make all variables and methods defined between the template class and class template available to the receiving class.
We illustrate the example below, making both editable boxes as editable ellipses. First we define the template class.
:- use_module(library(pce_template)). :- pce_begin_class(editable_graphical, template). :- pce_global(@editable_graphical_recogniser, make_editable_graphical_recogniser). make_editable_graphical_recogniser(G) :- Gr = @arg1, new(Dev, Gr?device), new(P, popup), send_list(P, append, [ menu_item(cut, message(Gr, free)), menu_item(duplicate, message(Dev, display, Gr?clone, ?(Gr?position, plus, point(10,10)))) ]), new(G, handler_group(new(resize_gesture), new(move_gesture), popup_gesture(P))). event(G, Ev:event) :-> ( send_super(G, event, Ev) ; send(@editable_graphical_recogniser, event, Ev) ). :- pce_end_class.
The main program can now be defined as:
:- require([use_class_template/1]). :- pce_begin_class(editable_box, box). :- use_class_template(editable_graphical). :- pce_end_class. :- pce_begin_class(editable_ellipse, ellipse). :- use_class_template(editable_graphical). :- pce_end_class. test :- send(new(P, picture('Template Demo')), open), send(P, display, editable_box(100,50), point(20,20)), send(P, display, editable_ellipse(100, 50), point(20, 90)).
Note that use_class_template/1 imports
the definitions from the template in the current class. Thus, the
following will not extend further on the `editable_graphical->
event'
definition, but instead replace this definition. Of course it is
allowed to subclass the definition of editable_box above and further
refine the event method in the subclass.
:- require([use_class_template/1]). :- pce_begin_class(editable_box, box). :- use_class_template(editable_graphical). event(Gr, Ev:event) :-> ( send_super(Gr, event, Ev) ; ... ). :- pce_end_class.
The XPCE/Prolog class-compilation is defined using the Prolog preprocessing capabilities of term_expansion/2. While the class is compiled, Prolog gathers the expressions belonging to the class. The expansion of :- pce_end_class(Class) emits the actual code for the class.
The method implementation is realised by the predicates pce_principal:send_implementation/3 and pce_principal:get_implementation/4. that take the form:
:- pce_begin_class(gnus, ... gnu(X, A:int) :-> ... gnats(X, A:name, B:int) :-> ...
is translated into
pce_principal:send_implementation('gnus$+$->gnu', gnu(A), O) :- ... pce_principal:send_implementation('gnats$+$->gnu', gnats(A, B), O) :- ...
The remainder of the class specification is translated into a number of Prolog clauses describing the class. No XPCE class is created. If XPCE generates an undefined_class exception, it will scan for the class-description in the Prolog database and create the XPCE class instance. No methods are associated with the new class. Instead, all method binding is again based on exception handling.
Modifications to the class beyond what is provided by the
preprocessing facilities (for example changing the `variable->
clone_style')
cannot be made by sending messages to the class inside the class
definition body as this would address the not-yet-existing class.
Instead, they should be embedded in the pce_class_directive/1
directive.9To facilitate the
translation of old code, the construct :- send(@class,
... is treated automatically as if it was embedded using pce_class_directive/1.
The Goal argument of pce_class_directive/1
should refer to the class using the XPCE var
object @class. When the class is
realised the exception system will bind @class
to the current class while running Goal. Goal is
called from the Prolog module in which the class is defined.
The main reason for the above approach is to exploit the runtime-generation facilities of the hosting Prolog system to create fast-starting portable and (depending on the hosting Prolog's capabilities) stand-alone executables.
One of the consequences of the chosen approach is that the class-building directives are not accessible as predicates. There is no preprocessing support for the dynamic creation of classes and the programmer should thus fall back to raw manipulation of the XPCE class objects.