Interactive Data Language (IDL) - Course by R.K.

5.Pointers and Objects

IDL knows to kinds of so called heap variables which are global in scope, which means they are conserved in memory until explicitly deleted and can be referenced by any program part. They actually have many similarities but serve different purposes.

5.1. Pointers

Pointers are used for slimline parameter passing and indirect acces of data. The are very useful with variable size structures. A pointer is created and linked to specific data with the ptr_new command.


b is now a pointer linked to the array a. To reference to its contents, it is preceded by an asterisk ('*').

IDL> print,*b
      0.00000      1.00000      2.00000      3.00000      4.00000

A pointer now can be passed to a subroutine to make a variable known there.

pro pointer1,pointer

Calling this routine now equally prints the array.

IDL> pointer1,b
      0.00000      1.00000      2.00000      3.00000      4.00000

Instead of passing the array a to the subroutine, we passed just one scalar value, thus saving memory.

Unlike C, pointer arithmetic is illegal.

A null pointer is provided for logical testing. Note the diference between a null pointer (which later cannot be given a valid address) and an undefined pointer.

IDL> a=ptr_new()
IDL> b=ptr_new(/allocate_heap)
IDL> c=findgen(10)
IDL> *b=c
IDL> *a=c
% Unable to dereference NULL pointer: A.
% Execution halted at:  $MAIN$

Arrays of pointers are created with the ptrarr command.

Pointers may point to pointers.

5.2. Objects

In order to maintain large programs they have to be broken in modules. In conventional programming these modules are subroutines or functions that perform well defined operations over a set of input data and return values to the calling program. This form of programming is oftenly called procedural programming.

Though procedural programming seems to be a 'natural' way of breaking programs into pieces it has shown to have some serious drawbacks. Error tracking often is problematic, errors tend to have consequences very far away from the point they occur, reuse of procedural programs is low.

Over the years a different form of programming has become increasingly popular, object oriented programming. In this case, theindividual modules are confined sets of data and procedures  (called methos) defined on them.  Although for the experienced procedural programmer this seems clumsy and inconvenient, it has been shown that the encapsulation yields higher reliability and better reuse.

5.2.1 Object Definition

To define an object in IDL first its characteristics have to be defined. This is called a class definition and done in a corresponding file. If the object class has the name fimage then it is defined in a file calledf (TWO underscores). Let us define a class of objects that are composed of a header and a twodimensional real data array 512x512 in size. The definition is done by a structure:

; define 512x512 fits image
pro fimage__define

result={fimage, header:strarr(36), data:fltarr(512,512)}

Now we have an object class 'fimage' from which we can stamp out individual objects, e.g.

IDL> image1=obj_new('fimage')
IDL> image2=obj_new('fimage')
IDL> help,image1,image2
IMAGE1          OBJREF    = <ObjHeapVar1(FIMAGE)>
IMAGE2          OBJREF    = <ObjHeapVar2(FIMAGE)>

However, all we can't do anything with these objects (except deleting them). We need some methods that work upon them. These should be declared in the same definition file, preferrably before the object itself in order to be compiled automatically when the object class is created. The declaration is done similar to ordinary procedures but by preceeding the method name with the object class and two colon.  Within the defining program access to the data structure is providid via a structure called self.
We will define two methods on our objects 'fimage', one that reads in data from a fits file, the other that visualizes the data.

; module to read 512x512 image
function fimage::read, filename
; check for correct size
if ( (result_size(1) ne 512) or (result_size(2) ne 512) or $
     (result_size(0) ne 2)) then return,-1
self.header=res_header(0:35 < n_elements(res_header)-1)
; display feature
pro fimage::display
; define 512x512 fits image
pro fimage__define
result={fimage, header:strarr(36), data:fltarr(512,512)}

Methods are called by the -> operator.   To read in and visualize an image we can now proceed

IDL> result=image1->read('./man.fits')
IDL> image1->display

Exercise: Create an object class of images independent of size. 

Objects are deleted by the obj_destroy,<object_name> command.

There are two implicit methods, init and cleanup that, if present, are invoked on creation or deletion of an object.

function incl::init
print,' variable initialized to 5

pro incl::cleanup
print,' object deleted'

pro incl__define

result={incl, x:0.}

Note that the init method is a function and has to return a value 'true'. If it returns 'false', a null object is created instead.

IDL> a=obj_new('incl')
 variable initialized to 5
IDL> obj_destroy,a
 object deleted

5.2.2. Inheritance

Object classes already defined can inherit definitons to other classes. Let us assume that we want to create objects basically the same as 'fimage' but they will be elements of a series

pro serial_fimage__define
result={serial_fimage, serial_number:0, INHERITS fimage}

Objects of this class have all the properties defined for the 'fimage' class, referring to data structures as well as methods.

5.2.3 Object Graphics

Most of  non widget graphics in IDL are available as objects. Except from an object oriented approach to plotting, the advantage of graphical objects is that they are device independent. Once completely defined they can be send to output devices such as screens or printers and should deliver identical results.

However, the price tag is high. IDL considers a graphical object tree to be a projection of a tree dimensional data space renders all objects within this space. This means that even most simple graphics will lead to enormous files.

A graphical object tree consists of a four level hierarchical structure:

A view is the smallest entity that can be realized on a screen or printer. Graphics atoms are analogous to the basic plotting routines in direct graphics, like plot,  polygons, surface, etc.  As an example let us draw a sine and cosine wave. First we create the basic plot objects. The corresponding object classes are called IDLgr<name> where 'name'  is the same as the name of the direct graphics procedure.

IDL> data=findgen(101)/100.*2.*!pi
IDL> sine=obj_new('IDLgrPlot',sin(data),linestyle=0)
IDL> cosine=obj_new('IDLgrPlot',cos(data),linestyle=2)

Now these objects have to be put into a model, a class 'IDLgrModel' provides methods like add or remove to do the job.

IDL> mymodel=obj_new('IDLgrModel')
IDL> mymodel->add,sine
IDL> mymodel->add,cosine

Models can be rotated in 3D space and then have to be put into views. In our example there is not much sense in transforming them, we put them immediately into the view. Some care has to be spend at this step. As models can be added or deleted at any time from the view, there is no reasonable default for the size of the viewplane. We will define it here so the plot fits in with some reasonalbe margin. The default units are data units.

IDL> myview=obj_new('IDLgrView',viewplane_rect=[-10,-1.2,120,2.4])
IDL> myview->add, mymodel

Now we are ready to visualize this structure. The IDLgrWindow class has a method draw that will do the job.

IDL> mywin=obj_new('IDLgrWindow')
IDL> mywin->draw,myview

All objects must be explicitly deleted with obj_destroy, just closing the window with the window controls maintains the window object in memory.  Observe that contrary to the 'plot' procedure no axis have been drawn. Axis must be added as object from the IDLgrAxis class.

To print the same graphics, the draw method has to applied on a IDLgrPrinter  class object. This may be configured to print to a postscript file instead of printing directly on a printer.