X
11183 Rate this article:
5.0

New in IDL 8.5, Function Pointers and Dynamic Methods

Jim Pendleton

One side effect of adding a Python language bridge to IDL in 8.5 is the exposure at the API level of a couple new ways to call IDL object methods.

Even if you never write code that takes advantage of these features, you will want to be aware of the implications of the new language syntax if you should ever encounter it in the handiwork of someone else.

The original motivation for the features was to expose Python syntax and constructs in an efficient way through IDL, specifically through classes that inherit from IDL_Object. Changes to the interpreter were made to support this.

These techniques are now documented for the public and can be used in your own code. They are available to you if you find their patterns appropriate for your own applications.

Function Pointers

Conceptually, a "function pointer" in IDL is just a new way to treat an IDL_Object reference as if it is a function.

For example, I may have defined a class "my_class" that inherits from IDL_Object. It's instantiated the usual way.

IDL> mine = my_class() ; or OBJ_NEW('my_class')

The new syntax allows me to "call" the object reference as if it is a function, if I have set COMPILE_OPT to IDL2 or STRICTARR first.

IDL> compile_opt idl2
IDL> result = mine(1,2,3)

At run time, the interpreter sees the object reference associated with the variable "mine" and notices the left parenthesis that immediately follows. The interpreter then checks if this object's class has implemented the method "::_overloadFunction", inherited from IDL_Object. If so, it then calls that method with the parameters and keywords in the argument stack. If the method is not implemented, an error is thrown.

The implementation of the method in "my_class" might generate a product of the three arguments, and would be implemented like this

function my_class::_overloadFunction, arg1, arg2, arg3
return, arg1*arg2*arg3
end

Left as an exercise for the reader is determining the call hierarchy if there is also, say, a "regular" IDL function with the same name as the variable represented by the object reference.

In the above code, consider the possibility that there is a standard IDL function that has the same name as the variable "mine", for example "FUNCTION mine, arg1, arg2, arg3".  Will that function be called or the object's ::_overloadFunction method?

Will the behavior depend on which routines have been compiled or resolved first?

Also left as an exercise for the reader is the test of using ::_overloadFunction when subclassing or adding to IDL_Object subclasses that already have built-in special syntax for interpreting left parentheses, such as LIST and HASH

Dynamic Methods

The IDL_Object::_overloadMethod represents a second new syntax introduced in IDL 8.5, also intended primarily to support the Python bridge. The intended functionality itself is described in the following way in the documentation.

By implementing _overloadMethod on your class, users can then make an arbitrary method call on an object reference.

Practically, the new behavior comes into play when the "." dot operator is encountered by the interpreter next to a variable or expression that is an object reference. In some ways this is analogous to the new behavior when a left parenthesis is interpreted and associated with the function pointer syntax described above.

Continuing with the same object created in the earlier example, let's say we have

IDL> data = mine.mymethod(1,2,3)

If I have a method on this class named "::mymethod", then it should be called first.  This is standard behavior in IDL 8.4 and earlier. However, if that method does not exist, rather than issuing an error there is a new behavior in IDL 8.5, an additional step.

If I have defined a method named ::_overloadMethod in "my_class", that method will be called. Its first positional argument will be the uppercase version of the string following the dot operator, up to but not including the left parenthesis. In this example, the string will be "MYMETHOD".

Any positional or keyword parameters are then passed through to the implementation of ::_overloadMethod in the standard way.

See the example in the IDL 8.5 online help associated with IDL_Object. As originally conceived, ::_overloadMethod is a mechanism for calling methods on objects referenced within the class rather than methods of the class itself. That is, the object implementing ::_overloadMethod is a proxy for contained objects.

However, the string that's passed as the "method name" may or may not actually be the name of a method in any class whatsoever.  It's just a string at the interpreter level.

A class could use the string being passed as "method name" in any way imaginable.

One might easily abuse this to produce unintelligible code, for example, where one simply doesn't care to use quotation marks.

The only rule is that the string must be valid as a theoretical method name, using appropriate characters.  For example, I can call a "method" named "ABC123", but not "123".

Consider this example

IDL> a = {myo, inherits idl_object}
IDL> .compile
- function myo::_overloadmethod, supposedmethodname, a, b, c
- help, supposedmethodname
- return, 0
- end
IDL> r = (myo())._o080o_()
SUPPOSEDMETHODNAME      STRING    = '_O080O'

The interpreter syntax change in IDL 8.5 also applies if the CALL_METHOD function is used instead, so one could dynamically create a "method" name to be called.

IDL> s = call_method(IDL_VALIDNAME(SYSTIME(), /CONVERT_ALL), myo())
SUPPOSEDMETHODNAME      STRING    = 'WED_JUL_29_12_40_37_2015'

Be kind to future generations of coders and use these new features wisely and after much consideration.