An important side effect of the IDL-Python bridge is to add some Python features to IDL in order to be able to effectively expose a Python API in IDL. One of these is the introduction of “function pointers”, a way of treating an object as a function. This is useful because objects have a lot of nice properties just from being a variable – they can be stored in a variable, passed to other functions, saved in a .sav
file, etc. With the introduction of function pointers, functions have these same properties.
To define an object to be used as a function pointer, a class needs to inherit from IDL_Object
and define an _overloadFunction
method:
function my_class::_overloadFunction, arg1, arg2, arg3, ...
Then, an object instantiated from this class can be called like a function and this method will be called to produce the result:
IDL> compile_opt strictarr
IDL> obj = my_class()
IDL> print, obj(1, 2, 3)
The “strictarr” or “idl2” compile_opt
is needed or IDL will use the _overloadBracketsLeftSide
method.
For example, let’s define a simple class that represents the histogram equalization image processing operation in mg_hist_equal__define.pro:
;= operators
function mg_hist_equal::_overloadFunction, im
compile_opt strictarr
return, hist_equal(im, percent=self.percent, top=self.top)
end
;= property access</p>
<p>pro mg_hist_equal::setProperty, percent=percent, top=top<br />compile_opt strictarr</p>
<p>if (n_elements(percent) gt 0L) then self.percent = percent<br />if (n_elements(top) gt 0L) then self.top = top<br />end</p>
<p>;= lifecycle methods</p>
<p>function mg_hist_equal::init, _extra=e<br />compile_opt strictarr</p>
<p>if (~self->IDL_Object::init()) then return, 0</p>
<p>self->setProperty, _extra=e</p>
<p>return, 1<br />end</p>
pro mg_hist_equal__define
compile_opt strictarr
!null = { mg_hist_equal, inherits IDL_Object, $<br />percent: 0.0, $
top: 0 }
end
The class defines a few properties that will be passed to the HIST_EQUAL
function when called, but to be a function pointer, all that is needed is to inherit from IDL_Object
and define the _overloadFunction
method.
As an example of using this class, let’s read in an image:
IDL> dims = [248, 248]
IDL> file = filepath('convec.dat', subdir=['examples', 'data'])
IDL> mantle = read_binary(file, data_dims=dims)
Then define our function pointer:
IDL> he = mg_hist_equal(percent=10.0, top=255)
We could just call our function pointer like this:
IDL> equ_mantle = he(mantle)
But instead let’s define a function that applies any function pointer to a variable:
function mg_function_pointer_demo, im, op
compile_opt strictarr
return, op(im)
end
Then we can pass our function pointer to this function:
IDL> equ_mantle = mg_function_pointer_demo(mantle, he)
IDL> window, xsize=dims[0] * 2, ysize=dims[1]
IDL> tv, mantle, 0
IDL> tv, equ_mantle, 1
This code is in mg_function_pointer_demo.pro and can be called with:
IDL> .run mg_function_pointer_demo
Function pointers have many applications in such areas where an algorithm has a callback function such as fitting and optimization algorithms. The syntax of function pointers would be cleaner than passing function names as strings and also allows the various parameters of the function represented by the function pointer to be set before passing it.
See the IDL Data Point article for more information.
August 11th, 2015 at 11:02 am
Hey Mike,
You mentioned the possibility of using function pointers where function names are required presently.
Assuming it wouldn’t work as-is, what would it take to modify the old standby IDL library routine CURVEFIT to accept a function pointer instead of a function name? I’d really like to pass some user value-like data along, such as boundary conditions. Could a function pointer be a way to achieve this?
Jim P.
August 11th, 2015 at 11:13 am
It looks pretty easy, there is a line:
which would become:
User data would then be properties/instance variables of the object. Currently, you have to use common blocks for that type of data; function pointers would be much nicer.
August 11th, 2015 at 11:16 am
And, of course, it could be made to be backward compatible by checking if
function_name
is a string or object.August 11th, 2015 at 9:59 pm
Thanks, Mike! I hates me the common blocks. Maybe someone at Harr-exel-is could make that mod for a future release of IDL.
August 11th, 2015 at 10:09 pm
That would be cool, if only I knew someone there who also wanted this change…
August 12th, 2015 at 8:37 pm
If only it were that easy. Sometimes one must even resort to two dozen “donuts of backward compatibility” just to prevent pre-release changes from breaking the real world. Even after 20 years, there’s no known path for getting new features.
August 18th, 2015 at 9:01 am
[…] of the side effects of the IDL-Python bridge is to add the ability to define dynamic methods (function pointers are the other). Dynamic methods are a way to define arbitrary methods for a class at […]
January 26th, 2016 at 5:06 pm
[…] 8.5 provides just such a mechanism — function pointers. Objects which inherit from IDL_Object and define an _overloadFunction method can be […]