At this point it is necessary to confess to a small amount of
deception. So far, we have been passing ObjectObject pointers to AST
functions in order to perform operations on those Objects. In fact,
however, what we were using were not true C functions at all, but
merely macros which invoke a related set of hidden functions with
essentially the same arguments. In practical terms, this makes very
little difference to how you use the functions, as we will continue to
call them.
The reason for this deception has to do with the rules for data typing in C. Recall that most AST functions can be used to process Objects from a range of different classes (§4.3). In C, this means passing different pointer types to the same function and most C compilers will not permit this (at least, not without grumbling) because it usually indicates a programming error. In AST, however, it is perfectly safe if done properly. Some way is therefore needed of circumventing the normal compiler checking.
The normal way of doing this in C is with a cast. This approach quickly becomes cumbersome, however, so we have adopted the strategy of wrapping each function in a macro which applies the appropriate cast for you. This means that you can pass pointers of any type to any AST function. For example, in passing a ZoomMapZoomMap pointer to astShowastShow:
we are exploiting this mechanism to avoid a compiler warning, because
the notional type of astShow's parameter is AstObject (not
AstZoomMap
).
We must still guard against programming errors, however, so every pointer's type is checked by the enclosing macro immediately before any AST function executes. This allows pointer mis-matches (in the more liberal AST sense—i.e. taking account of the class hierarchy, rather than the stricter C sense) to be detected at run-time and a suitable error message will be reported. This message should also identify the line where the error occurs.
A similar strategy is used when pointers are returned by AST functions
(i.e. as the function result). In this case the pointer is
cast to void, although we retain the notional pointer type in the
function's documentation
(e.g. Appendix B). This allows you to
assign function results to pointer variables without using an explicit
cast. For example, the astReadastRead function returns an Object pointer, but
might be used to read (say) a ZoomMap as follows:
Strictly, there is a C pointer mis-match here, but it is ignored because the operation makes perfect sense to AST.
There is an important exception to this, however, in that
constructor functions always return strongly-typed pointers. What
we mean by this is that the returned pointer is never implicitly cast
to void. You must therefore match pointer types when you initially
create an Object using its constructor, such as in the following:
If the variable receiving the pointer is of a different type, an appropriate cast should be used, as in:
This is an encouragement for you to declare your pointer types consistently, since this is of great benefit to anyone trying to understand your software.
Finally, we should also make one more small confession—AST pointers are not really pointers at all. Although they behave like pointers, the actual “values” stored are not the addresses of C data structures. This means that you cannot de-reference an AST pointer to examine the data within (although you can use astShow instead—§4.4). This is necessary so that AST pointers can be made unique even although several of them might reference the same Object.