Extracting Object Fields
QUESTION: I would like to write a general extraction method for IDL objects. But, unfortunately Tag_Names doesn't work for objects, only structures. Is there a way to write a general extraction method for objects that can return any field of the object?
ANSWER: Even though objects are very much like structures, routines such as Tag_Names don't work with them. It is possible, however, to get to the underlying structure of an object and extract its fields in a general way.
In the little example program below I use the Obj_Class function to return the class name of the object. This is essentially the name of the structure from which this object was created. But the name is in the form of a string variable. To turn that into a real structure, from which I can extract the field in question, I use the Execute command to "build" the structure de novo. The code looks like this:
FUNCTION OBJECT::EXTRACT, field ; Check if "field" is a valid field name. If it is, ; return the value of the field. If not, return -1. ; Make sure "field" is a string variable. s = Size(field) IF s[s[0]+1] NE 7 THEN Message, "Field variable must be a sting." ; Get the name of the object class. thisClass = Obj_Class(self) ; Create a local structure of this type. ok = Execute('thisStruct = {' + thisClass + '}') ; Find the field identifier (index) in the local structure. structFields = Tag_Names(thisStruct) index = WHERE(structFields EQ StrUpCase(field), count) ; Extract and return the field if it is found. IF count EQ 1 THEN BEGIN RETURN, self.index(0) ENDIF ELSE BEGIN Message, 'Can not find field "' + field + $ '" in structure.', /Informational RETURN, -1 ENDELSE END
To find, for example, the field "range" in this object, you would type this:
thisRange = Object->Extract("range")
J.D. Smith of Cornell University offered suggestions that made this example program even more simple and elegant.
Several months after I first wrote this article, Bob Mallozzi of the NASA Marshal Space Flight Center wrote to me with some further helpful suggestions. I include his entire e-mail to me here because I think it illustrates extremely well the evolution of good programs. I personally think his suggestions are excellent, especially his point about not violating the encapsulation rules of objects.
Date Sat, 11 Apr 1998 084758 -0500 (CDT)
From "Robert S. Mallozzi"
Subject Object fields
Hello David,
I was just looking at one of the helpful tips you have posted on your web page entitled "Extracting Object Fields." I wanted to mention a couple things regarding the code you have there. The part of the code is the following:
structFields = Tag_Names(thisStruct) index = WHERE(structFields EQ StrUpCase(field), count) ; Extract and return the field if it is found. IF count EQ 1 THEN BEGIN RETURN, self.index(0) ENDIF ELSE BEGIN Message, 'Can not find field "' + field + $ '" in structure.', /Informational RETURN, -1 ENDELSE
You are returning the value "self.index(0)", but the tag name "index" is not the tag name that we want for this object. Another EXECUTE will solve the problem.
structFields = Tag_Names(thisStruct) index = WHERE(structFields EQ StrUpCase(field), count) ; Extract and return the field if it is found. IF (count EQ 1) THEN BEGIN IF (EXECUTE ('retVal = self.' + structFields[index[0]])) THEN BEGIN ; Don't ever return a valid pointer into an object ; IF (PTR_VALID (retVal)) THEN BEGIN RETURN, *retVal ENDIF ELSE BEGIN RETURN, retVal ENDELSE ENDIF RETURN, -1 ENDIF ELSE BEGIN Message, 'Can not find field "' + field + $ '" in object ' + OBJ_CLASS (self), /Informational RETURN, -1 ENDELSE
Also, there is something else I would like to point out. Notice the check in the code above to make sure we dereference a pointer before returning it. One should never return a valid pointer into to an object, as that breaks encapsulation by allowing access to the object's internal data without going through a defined object method. For example, by returning a (non-null) pointer one can do the following type of things.
IDL> o = OBJ_NEW ('example') IDL> o->PRINT instance_data_int = 10 instance_data_float = 20.0000 instance_data_ptr = 30.0000 IDL> myPtr = o->extract ('instance_data_ptr') IDL> PRINT, *myPtr 30.0000 IDL> o->PRINT instance_data_int = 10 instance_data_float = 20.0000 instance_data_ptr = 30.0000 IDL> *myPtr = 100 IDL> o->PRINT instance_data_int = 10 instance_data_float = 20.0000 instance_data_ptr = 100
Here, I actually changed the datatype of the object's instance data from outside the object.
Regards,
Robert S. Mallozzi
Copyright © 1997-1998 David W. Fanning
Last Updated 11 April 1998