At this point it is necessary to confess to a small amount of
deception. So far, we have been passing Object 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 (). 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 ZoomMap pointer to astShow:
AstZoomMap *zoommap; ... zoommap = astZoomMap( 2, 5.0, "" ); astShow( zoommap );
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. ). This allows you to
assign function results to pointer variables without using an explicit
cast. For example, the astRead function returns an Object pointer, but
might be used to read (say) a ZoomMap as follows:
AstChannel *channel; AstZoomMap *zoommap; ... zoommap = astRead( channel );
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:
AstZoomMap *zoommap; ... zoommap = astZoomMap( 2, 5.0, "" );
If the variable receiving the pointer is of a different type, an appropriate cast should be used, as in:
AstMapping *mapping; ... mapping = (AstMapping *) astZoomMap( 2, 5.0, "" );
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--). This is necessary so that AST
pointers can be made unique even although several of them might
reference the same Object.
AST A Library for Handling World Coordinate Systems in Astronomy