10.3 Defining global named objects

As explained before, named references should be restricted to debugging and reusable objects. A couple of problems related to their creation and usage can be identified:

Various alternatives to creating global objects have been tried. We will present some of the straight-forward approaches below and describe the (dis)advantages. Section 10.3.3 describes the pce_global/2 directive to solve this problem. We will use a particular fill-pattern (image) as an example.

10.3.1 Using directives

Using a directive to create a reusable global object appears to be a logical way of dealing with them. This leads to the following code:

:- new(@stones_image, image(stones)).

        ...,
        send(Box, fill_pattern, @stones_image),
        ...

This code has a serious disadvantage. Whenever this file is reloaded after the Prolog code inside it has changed, the directive will be executed as well. The predicate new/2 will generate a warning on an attempt to create an object with an existing name.

10.3.2 Inline testing

Another common approach is to test inline. For the example, this would look like:

        ...,
        (   object(@stones_image)
        ->  true
        ;   new(@stones_image, image(stones))
        ),
        send(Box, fill_pattern, @stones_image),
        ...

This approach is bad programming style. If @stones_bitmap is required at various places in the source files this construct needs to be repeated at various places.

10.3.3 The `pce_global' directive

This approach is based on exception-handling. If PCE translates a named reference into an internal reference and the named reference does not exist it will raise an exception. The pce_global/2 directive installs an exception handler dealing with a specific global reference. The example becomes:

:- pce_global(@stones_image, new(image(stones))).

        ...,
        send(Box, fill_pattern, @stones_image),
        ...

This directive applies some heuristics to take care of redefinitions when the file is reconsulted: if the definition is still the same it will not complain. If the definition has modified and the object is already created it will rename the object using `object ->name_reference'. A later reference to the object will trap the exception handler again, creating a new object according to the current specification. The directive prints diagnostics messages on redefinitions and other possible problems during compilation. See appendix D for details on pce_global/2.

10.3.4 Global objects for recognisers

Recogniser objects (see section 5.5) that make graphical objects sensitive to mouse events are often created with a global reference. Suppose an application requires box objects that can be moved, resized and that have a popup menu. The recogniser may be defined as:

:- pce_global(@mybox_recogniser, make_mybox_recogniser).

make_mybox_recogniser(R) :-
        Gr = @arg1,
        new(P, popup),
        send_list(P, append,
                  [ menu_item(cut, message(Gr, free))
                    ...
                  ]),
        new(R, handler_group(new(resize_gesture),
                             new(move_gesture),
                             popup_gesture(P))).

This recogniser object may be attached to the graphical objects either using `graphical->recogniser' or by redefining the `graphical->event' method. In the first case, the recogniser is an integral part of the graphical object and cannot be changed easily during the lifetime of the object. In the second case, the reference to the gesture is through the Prolog implementation of the method and replacing the global object will make all members of the class behave according to the new definition immediately.

If the latter approach is taken and the recogniser is under development, you may wish to use free/1 to make sure the new definition is created:

:- free(@mybox_recogniser).
:- pce_global(@mybox_recogniser, make_mybox_recogniser).