X
9134 Rate this article:
No rating

Now you can write your own static variable methods

Anonym

 Static methods and properties have been in IDL for a while, and they have certainly become useful at times. They serve as a convenient, modern alternative to traditional routine-based IDL calls. You may have created your own class with static methods, but did you know you can add your own methods to IDL's classes? This is most useful with IDL's static methods on variables.

Is a variable greater than 10?

Let's try a basic example. I have an IDL variable, and I'd like to know whether that variable is greater than 10. I'll start with writing a new method on IDL_Variable, which I will call ::IsGreaterThanTen

function IDL_Variable::IsGreaterThanTen, var

Although this method will take no arguments when called statically from a variable, the method will contain one argument, which is the input variable, in this case var.

result = variable.IsGreaterThanTen()

As the name suggests, this method will need to be defined as static. This is done in the compile_opt statement. As always, it is advisable to use "idl2" in the compile_opt statement as well, and if you want the method to appear built-in, then also use "hidden."

The rest of this method is straightforward. First check that the variable is indeed a number, and then perform the inequality, returning the result.

function IDL_Variable::IsGreaterThanTen, var
  compile_opt idl2, hidden, static
  
  ; If it is not a number, then it is not greater than ten.
  if ~Isa(var, /NUMBER) then begin
    return, 0b
  endif
  
  return, var gt 10
  
end

State or province?

Here is a more useful example. A while back, I needed a way to determine if a given string was a valid abbreviation for a U.S. state or Canadian Province. This is a time where writing a new static method on IDL_String would be convenient.

function IDL_String::IsaStateOrProvince, str
  compile_opt idl2, hidden, static
  
  abbrevList = ['AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', 'FL', $
    'GA', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', $
    'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', $
    'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', $
    'VA', 'WA', 'WV', 'WI', 'WY', 'AB', 'BC', 'MB', 'NB', 'NL', 'NT', 'NS', $
    'NU', 'ON', 'PE', 'QC', 'SK', 'YT']

  !null = Where(abbrevList.Matches(str), count)
  return, count gt 0
  
end

(Note that technically some of these are territories and not provinces. DC is in this list, too, but this is the functionality I desire.)

Benefits

Now I can add this to my personal code library. I prefer this to a library of many different, possibly unrelated routines because it is a sensible way to bundle static method code into the same file (i.e. keep code related to IDL_String in the same file). This makes it easier for me to find and keep track of the code in the future. Additionally, I find it more intuitive to call

result = state.IsaStateOrProvince()

instead of a traditional routine call of

result = IsaStateOrProvince(state)

I think it might be because the static way of calling reads left-to-right.

This concept is the topic of this blog I wrote a couple of years ago.

Also, moving the routine into a specific class frees up the main namespace. For example I could write a different IsaStateOrProvince routine that does something different, and similarly, I could write an ::IsaStateOrProvince method on a different class. Neither of these would not interfere with the example above.

Drawbacks

One minor drawback with writing a routine as a static method is that you can’t call the method directly on a value that isn’t a variable, meaning you can't call

result = 'NY'.IsaStateOrProvince()

because you would get a syntax error. However, if you simply add "IDL_String" plus the "dot syntax" in front, then it would work:

result = IDL_String.IsaStateOrProvince('NY')

This is different than overloading a class. Overloading a class involves writing a new subclass that inherits the class and then overloads the inherited class's methods and/or adds new methods. Some of IDL's classes, such as IDL_Object, serve the purpose of being used as a superclass for user-written classes. Although IDL variable classes aren't specifically intended to be overloaded, you can overload any class you'd like.

A separate blog post is dedicated to overloading IDL's built-in classes, which provides an example of overloading IDL's LIST.

Warning! Do not save this into a file called IDL_String__Define or IDL_Variable__Define or any other static classname. Instead, you will need to place this into a differently named file that serves as a library file. Therefore, you may need to compile the file using RESOLVE_ROUTINE or restore it from a savefile at the start of your application. In other words, treat it as a library file that contains multiple routines inside of it.

As an example, I have saved my IDL_String::IsaStateOrProvince method into a file called "bens_strings.pro" which will probably never conflict with other filenames in IDL.

Additional disclaimer: It is important to keep in mind that you are still sharing namespace with IDL, and IDL’s static methods should never be clobbered by user-written ones. You need to be very careful when naming new methods to make sure that their names do not conflict with existing IDL methods. IDL may introduce new static variable methods in future releases, which may then conflict with something you’ve written. Therefore, it is important to check the “What's New” help topic when installing a new version of IDL.

This is, of course, true for any user-written code because you are always sharing namespace with IDL, but at a low-level like this, it is extra important.

I would probably never recommend trying to overload the::GetProperty method of any of these static classes in order to try to add your own static properties. Just stick with methods.

 

Edit: I have recently learned that it is also possible to put a method into a file called [classname]__[method_name].pro. For instance in the example above, I could write a file called idl_string__isastateorprovince.pro (two underscores between the classname and method name), and IDL will automatically compile it. This saves having to manually compile/restore a helper file.