Coyote's Guide to IDL Programming

Adding INIT or CLEANUP Methods to Objects

QUESTION: I've written an INIT (or CLEANUP) method for my object, but it never runs even though I can see the function or procedure is compiled. Why not?

ANSWER: Yes, this exceedingly strange and completely undocumented behavior has been a feature of objects in all versions of IDL through IDL 5.2.1, which was current when this article was written. The INIT and CLEANUP methods are special object methods, called lifecycle methods. For some reason, the lifecycle methods that get attached (or not) to an object class when the first instance of that object class is created are the methods associated with that object class for the entire IDL session. [Please see the Addendum at the end of this article for additional information.]

Let me say that again.

The lifecycle methods that get attached (or not) to an object class when the first instance of that object class is created are the methods associated with that object class for the entire IDL session.

Let's examine some of the consequences of this fact.

Suppose you create an object class JUNK that has several pointers associated with it. You write the object definition module, JUNK__DEFINE, and then the INIT method for the object, JUNK::INIT, but you fail to write the JUNK::CLEANUP method. Now, you create an instance of this object class:

   thisObject = Obj_New("JUNK")

But when you destroy it, you notice that you have some memory leakage going on because you forgot to destroy the pointers in a CLEANUP method:

   Obj_Destroy, thisObject
   Help, /Heap
         BYTE      = Array[256, 256]
         BYTE      = Array[360, 360]

So you decide to write the CLEANUP method. After you do so, you recompile your program, create a new object and destroy it, and you still have memory leakage. After noodling around for 20 minutes, you put a PRINT statement in your CLEANUP method and you notice that when you destroy the object the CLEANUP code is not being called. Why not?

Simply put: Because the CLEANUP method was not associated with that object class when the very first object of that class was created in that IDL session. The only way to get that CLEANUP method associated with an object of that class is to exit IDL and create a new object when you restart.

This is also true of an INIT method. It is not true of any other method. Any non-lifecycle method can be added or modified at any time in the IDL session and its effect noticed immediately. Only the two lifecycle methods INIT and CLEANUP have this strange limitation.

How can you not get an INIT method attached to an object?

Two ways, primarily. First, suppose you inherit a superclass object, say the object JUNKER. But, before you create an INIT method for your JUNK object, you create a JUNK object. IDL realizes that the JUNK object doesn't have an INIT method, so it searches for the INIT method of a superclass. It finds one in JUNKER. But now this JUNKER::INIT method is attached to the JUNK object class. If you decide you do need an INIT method for JUNK, you will have to exit IDL and restart to get the right INIT method associated with the JUNK object class.

The second way is the more pernicious. Suppose you create an INIT and CLEANUP method for your junk object class, and everything compiles. Now, suppose you create your first instance of a JUNK object. But you have made a programming mistake in the INIT method and it crashes. Ooough, bad luck here!

Now you have created an object with no way to attach an INIT method. Yup. You guessed it. You have to exit and start over. That INIT method had better work the first time you try it! (They never do, obviously, but do pay very careful attention to your code, so you don't have to go through too many iterations.) After you have gotten a lifecycle method safely attached to an object (i.e., the INIT method worked correctly), then you can safely make changes to that lifecycle method in that IDL session. Recompiling the code and recreating the object works as you expect. Note that you can not make changes to the object definition module without exiting IDL, at least in all versions of IDL through IDL 5.2.1.

(I'm told that it is not absolutely necessary to exit IDL when the INIT method crashes. That judicious and frequent (more than once) use of RETALL and HEAP_GC can put things right. I haven't tried it. I still think exiting IDL is the best solution. The important thing is to be aware that you have to do something or you will not be getting what you expect to get.)

ADDENDUM: Since this article was first written two things have come to my attention. First, it is no longer necessary to exit the IDL session to attach these methods to the objects. The new executive command .Reset_Session, introduced with IDL 5.3, can be used instead to set the IDL session back to its default values. This command has been a godsend for those of us working with objects and named structures on a daily basis!

Second, Mark Hadfield has brought to my attention that this whole issue of methods not getting attached to the proper object applies not just to lifecycle methods, but to all methods.

For example, suppose I have a CIRCLE object and I create it without first defining a DRAW method for it. No problem. I code up the CIRCLE::DRAW method, recompile my code, re-create the object, and I can draw it to the display. But suppose I now create a FILLEDCIRCLE object, which inherits the CIRCLE object. If I create a FILLEDCIRCLE object without first writing a new FILLEDCIRCLE::DRAW method to draw a filled circle instead of an unfilled circle, it will be the CIRCLE::DRAW method that is attached to my FILLEDCIRCLE object.

Now, even if I write the FILLEDCIRCLE::DRAW method and re-compile the code and re-create the object I will still be calling the CIRCLE::DRAW method! There will be no way to attach the FILLEDCIRCLE::DRAW method to the object without either exiting IDL or resetting the IDL session with the .Reset_Session command.

Web Coyote's Guide to IDL Programming