X
6370 Rate this article:
5.0

A technique for flexible Get/SetProperty methods in IDL 8

Anonym
(Note: This is a guest post by Ron Kneusel, a Senior Consultant in the Exelis VIS Professional Services Group. I frequently bug Ron with questions about computer science, IDL and Linux. –MP) IDL's new hash table object, along with some sneaky programming, makes it possible to implement very flexible GetProperty and SetProperty methods for your classes. In this post, we'll present the code and describe how it works. The code here is built from that found in Ronn Kling's book Object-Oriented Programming with IDL starting on page 28 and following. Ronn points out that the first person to perform a similar trick was David Fanning, on whose code Ronn built. We are here adding to the chain. For my classes I use a hash as the only member variable. In earlier versions of IDL it was my own IDL_Collection object which has some nice features that hashes don't have (ask if you are curious) but for now, hashes work just as well and support a more intuitive syntax. In order to use the new Get/SetProperty methods in a class, we have to first tell the class to use the hash:
pro myclass__define
   compile_opt idl2, logical_predicate

   _ = {myclass, $
      d : obj_new() $  ; holds properties and instance vars
      }
end
Here we are defining a single member variable, ‘d’, which will be initialized to a hash in the Init() method:
function myclass::init
   compile_opt idl2, logical_predicate

   self.d = hash()
   return, obj_valid(self.d)
end
At this point, we can use the hash to store what would otherwise go in separate member variables. This flexibility allows us to add member variables at will without changing the class definition. We could even add them at run time. One caveat is that we have to put parentheses around 'self.d' before indexing. I usually make the first executable line of my methods 'd = self.d' so that I do not need to use cumbersome parentheses. Those of you who recall the old Apple II+ computer days may here see a nod to the classic basic trick of setting 'D$' to the control character for floppy disk access. At this point, we have defined our class and our constructor. We can explicitly define a destructor (Cleanup) but with IDL's new automatic garbage collection this is not really necessary in most cases. Now on to the fun part! First I'll present the SetProperty method which is more straightforward:
1 pro myclass::setproperty, _extra=extra
2   compile_opt idl2, logical_predicate
3
4   d = self.d
5
6   if (~n_elements(extra)) then return
7   n = n_elements((tags=tag_names(extra)))
8
9   for i=0, n-1 do begin
10     d[tags[i]] = extra.(i)
11  endfor
12 end
In line 1 we define the procedure. In this case, we only need the _EXTRA keyword to collect the actual keywords supplied. Line 2 contains our compiler directives. I strongly recommend using this set in each procedure or function. Line 4 is our shortcut for accessing our member data as described above. Line 6 simply asks if there are any keywords present and returns to caller if the answer is no. Line 7 gets the names of the keywords ('tags') and the number of keywords ('n'). Lastly, lines 9 through 11 iterate over the keywords and assign their values to the hash table. Line 10 is the one to focus on. We use the alternative syntax for indexing into the elements of an IDL structure (the 'extra' containing the keywords) and assign those values to the member variable hash using the corresponding tag as the key. After executing a call to this method, the member variable hash is updated. Note that the way we defined the class means that all other member variable values are also in the same hash. This means that names must be unique otherwise unintended corruption of member data may occur. OK, we've added the ability to set properties. This is useful but only limitedly so if we can't access those properties outside of the class. Here's the GetProperty method:
1 pro myclass::getproperty, _ref_extra=extra
2   compile_opt idl2, logical_predicate
3
4   d = self.d
5   if (~n_elements(extra)) then return
6
7   for i=0, n_elements(extra)-1 do begin
8      (scope_varfetch(extra[i], /ref_extra)) = d[extra[i]]
9   endfor
10 end
Lines 1-4 should be familiar. The only trick here is that we use _REF_EXTRA and not _EXTRA because we want to update the keyword values. Line 5 simply returns if there is nothing to do. The small loop of lines 7-9 contains the important bits. We loop over all the keywords given and look them up in our member data hash. This is the right-hand side of line 8. The left-hand side of line 8 is more mysterious and requires some explanation. The IDL function SCOPE_VARFETCH is, as Ronn Kling says in his book, "nearly magical." It gives the programmer a way to peek under the hood of IDL and to query and update variables that do not reside in the scope of the present function or procedure. Here we use its even more magical REF_EXTRA keyword which gives the programmer the ability to update a particular keyword by name. Since this is exactly what we want, this works out nicely. So, the LHS of line 8 can be thought of as one of the variables in the keywords given to GetProperty. With this, it is straightforward to see why GetProperty actually works. This is a powerful, if somewhat cryptic, way to let IDL update the values of an arbitrary number of keyword arguments. The code above is, of course, missing most error checking. This is for clarity and a serious use of this technique would put it in. For example, what happens if the user calls GetProperty with a name that isn't in the hash table? A casualty of this technique is the classic IDL trick of using short keyword names.  Here, we must use the entire name at all times as the name is simply a key into the hash table. This, while sometimes verbose, is probably a good thing in the long run. So, if the keyword is supposed to be 'MyLongKeywordName' don't use 'My' in one place and 'MyLongKeywordName' in another as the hash will treat these as two separate keywords. Since we've mixed our properties and member variables, we have a way to get or set any member variable at any time from outside an instance of the class. True, if we are within another method we can simply use the (previously officially unsupported) "." syntax to access the hash table and use it, but good programming practice encourages the use of get/set methods. With the above, we can continue to use the Get/SetProperty approach already typically used in object-oriented IDL programs. As a last note, there are, naturally, many ways to expand and adapt this technique. How you use it will depend upon the needs of your programs. In truth, I usually put all the above in a class I call 'Properties' and simply inherit from it in my own classes. If I also inherit from IDL_Object I can use the overloaded "." to call these routines, too.