IgorOOP /IgorOOPBasics

Top
Up
Contents
RecentChanges
Attachment
Backlinks
Print
Page
Edit
User:38.103.63.18
(anonymous)
Login
8331 hits since Sun Jun 2 21:37:10 2002

Instance?

The basic idea of implementing OOP in Igor is to regard a datafolder as an instance.
object (instance) = datafolder 
String variables, numeric variables, waves are going to be the member properties of the instance.
 - string /g -> string member 
 - variable /g -> numeric member 
 - make -> numeric wave member 
 - make /t -> text wave member 
These members are defined in a igor function with name classname_init(), e.g.
function foo_init() 
  string /g bar; 
  variable /g bar2; 
  make /n=10 wvar; 
end 

oop_new function accepts a path and class name, and create a datafolder with appropriate initialization.

Function  oop_new(path, classname) 
	string path, classname 
 
	// check for class existence 
	if (exists(classname+"_init") == 0) 
		print "class:", classname, " does not eixst" 
		return -1 
	endif 
	 
	// remember current datafolder 
	string df = getdatafolder(1); 
	 
	// strip off ":" (igor isn't flexible (nor consistent:) in handling pathnames) 
	if (cmpstr(path[strlen(path)-1] , ":") == 0) 
		path = path[0, strlen(path)-2] 
	endif	 
 
	// create datafolder 
	newdatafolder /o/s $(path) 
 
	// inherit Obj class 
	// create string variable 'class', wave 'parents' 
	obj_init(classname) 
 
	// call initialization functions 
	execute (classname+"_init()") 
	 
	// restore datafolder 
	setdatafolder df;	 
	return 0 
End

So the remaining problem in doing basic OOP is how to implement the inheritance and the polymorphism of method call.

Inheritance

Inheritance is accomplished by calling obj_inherit("parent") in an init function. E.g.
function foo_init() 
  obj_inherit("bar") 
  string /g foo1 
  variable /g foo2 
  ... 
end
obj_inherit just calls parent class's init function and add parent class name to a text wave called parents.

Function obj_inherit(parentname) 
	string parentname 
	Execute( parentname+"_init()")	// initialization (parents order matters) 
	util_appendToTWave("parents", parentname) // remember parent class 
End 

This way, members of parent classes get created and initialized and parent class names are stored in a special text wave named "parents" in the current datafolder.

(By the way, there is a special string variable named class which keeps class name of the current datafolder. So these names are reserved.)

As can be seen from this implementation, there is no metaclass. Each object keeps track of its own class and inheritance.

Method call

2004/01/03 : virtual method call is now implemented in series of oop_call functions, which are way better than oop_do. This part of the document will be updated in the future.

Method call is impolemented in a function oop_do. It accepts a path to the datafolder, method name, and arguments converted to a string. E.g.

oop_do("root:foo", "bar", "()") 
The class where foo belongs or the parent classes of foo must have bar method. A method of a class is a function with a name classname_method.
method = function classname_methodname(...) 
oop_do first look for a function with the name classname_method in the class which the object belongs, if it fails then it looks for the method in parent classes. If it find an appropriate function, it set current datafolder to the object's datafolder and it calls the method function using execute.

Function oop_do(path,  method, args) 
	string path, method, args 
	 
	variable count 
	string df = getdatafolder(1) 
 
	if (! datafolderexists(path)) 
		print "object:",path," does not exists." 
		return -1 
	endif 
 
	setdatafolder $(path) 
	SVAR class = class 
	if (exists("class") != 2) 
		print path, " is not a class instance!", ", method = ", \ 
                             method, "args=", args 
		return 0 
	endif 
	 
	wave /t parents= parents 
	 
	// get function name 
	string fname = ""; 
	if (exists( class+"_"+method) == 6)// first check "classname+method" 
		fname =  class+"_"+method; 
	else 
		// no function w/ "classname+method" check "parentname+method" 
		for (count =numpnts(parents)-1; count > -1; count -= 1) 
			if (exists(parents[count]+"_"+method) == 6) 
				fname = parents[count]+"_"+method 
				break 
			endif 
		endfor 
	endif 
	if (cmpstr(fname, "")==0) 
		print "No method exists: obj=", path, " class=", class, " \ 
                       method=", method, args 
		setdatafolder df 
		return -1 
	endif 
 
	// call method 
	Execute(fname+args) 
	 
	if (datafolderexists(df)) 
		setdatafolder df	 
	else 
		setdatafolder $("root:") 
	endif 
	return 0 
End 
You have to pass arguments in a string form. This is really awkward, cumbersome erroneous and unreadable. But I couldn't find a better way, so I usually tend to pass no argument (just pass "()") and set members (using oop_set_* functions) before calling oop_do.

An alternative to the above solution is described in OOP file (oop_getFname(obj, method)). oop_getFname returns function reference, so you don't have to encode arguments in a string and you can get return value. But since Igor doesn't allow variable number of arguments (am I wrong?), I couldn't put the whole sequence in one function, so every time you want to call a method, you have to

  1. get function ref with oop_getFname
  2. set current datafolder to the object's datafolder
  3. call the method function
  4. restore current datafolder
which is probably more cumbersome than the first one.

Accessor

From method

Since oop_do sets the current datafolder to the object's datafolder before method call. Method functions can access members using following igor definitions.
  • SVAR strmembername (for string member access)
  • NVAR varmembername (for variable member access)
  • wave wavename
  • wave /t textwavename

From outside

  • oop_get_s(path, name)
  • oop_get_v(path, name)
  • oop_set_s(path, name, value)
  • oop_set_v(path, name, value)
Or you can just use SVAR, NVAR with fullpath. e.g.
 SVAR foostr1 = $("root:foo:str1") 
etc. 

Pitfalls

  • Not suited for small & numerous object (many datafolders)
  • oop_do : not suited for frequent call (like in huge "for" loop)
  • oop_do : requires an argument converted to a string (better use accessors)

But completely OK for opposite cases (infrequent call, no argument, small number of objects, ...).

Reserved Names

class string keeps class name
parents text wave keeps parents' class names
path string path (in a harddisk) where the object will be saved
lpath string path (in a harddisk) where the object is loaded from
id string id (created from path to the datafolder) of the object