Re: IDL Object for creating a Singleton [message #11979] |
Mon, 22 June 1998 00:00 |
J.D. Smith
Messages: 214 Registered: August 1996
|
Senior Member |
|
|
David Fanning wrote:
>
> Phillip David (pdavid@earthling.net) writes:
>
>> I've been doing a fair amount of reading on OO design and methodology. One of
>> the books I've had pointed out to me was 'Design Patterns', which deals with
>> OO algorithm design quite nicely. One of the patterns the authors describe is
>> a Singleton -- A class for which only one instance exists. If the sole
>> instance exists, the constructor function (object's init function in IDL) will
>> return a reference to the existing object. If none exists, a new object is
>> created and returned.
>>
>> This structure is particularly useful when there is no reason to have multiple
>> objects around. I am trying to create a user preferences object that fits
>> this criteria very nicely. I'd like to be able to store user preferences for
>> screen size in an object. If that object exists, other screens can size
>> themselves appropriately. The book gives sample C++ code that relies on class
>> variables and class methods (i.e., not associated with a specific instance of
>> the object, but with the object itself). IDL doesn't seem to have any way to
>> do something similar. Does anyone know of a way to do this?
>>
>> My first attempt uses a common block called preferencesclassvars to store an
>> object reference. This common block is called from a function that, while not
>> part of the object, is stored in the same file. If the reference contained in
>> the common block is not an object reference, then I know the object hasn't
>> been initialized, and I set it to a new instance of the object. Otherwise, I
>> return the object reference from the common block.
>>
>> Is there a better way to achieve the techniques of class variables and class methods?
>
> Humm. Objects are turning out to be not only to be fun and powerful to
> program, but synchronistic as well. This is the second time in two
> days the _Design Patterns_ book (which I have not seen or read) was
> mentioned to me and in an almost identical context. I was talking
> to a friend about writing a "lurker" object, a singleton object like
> Philip's screen preferences object, that lurks in the background
> waiting for something to happen. In this case, I wanted to
> update all the open windows when a color table was changed on a
> 24-bit display.
>
> This wonderful idea suffers from other unsolvable (so far)
> problems, but I did manage to get it started up. Like Philip,
> I had to resort to a common block to initialize it properly.
> Since common blocks are never my preferred solution, and since
> this thing reeks of a hack, I'm also looking for something more
> elegant. I'm afraid I am swimming in programming space that
> is a little out of my depth. Perhaps someone like JD can throw
> both Philip and me a life-line. :-)
>
> Cheers,
I think you've both run head-on into the limits of IDL's OO
implementation. The foremost among these is the lack of controllable
access. All class members are equivalent to "protected" -- visible to a
class plus any derived classes. Especially frustrating is the absence
of public data members, which requires one to set up GetProperty and
SetProperty methods which do *nothing* other than access otherwise
"public" data.
Protected class methods, although less frustrating, can limit the amount
of cooperative programming possible for large projects (since no
mechanism is provided to prevent accidental reliance on internal, and
therefore potentially changing, class implementation).
But one must keep in mind that RSI wanted to provide the basic OO
functionality, not a totally robust OO environment. I think they did an
O.K. job with this. Polymorphism is well-implemented since all methods
are "virtual protected", and all objects (read object references) are
the equivalent of object pointers in C++ ... there is no such concept as
static typing of a method (just as there is no concept of static typing
of a variable) in IDL. Encapsulation, though rigid, accomplishes the
basics of the principle.
In any case, as to your specific problem, I haven't seen the book you
reference, but I'd bet they achieve the "singleton" functionality with a
static data member -- a variable which is shared by all instances of the
defining class, but only initialized once. There is no comparable
functionality in IDL. I believe the only way to implement this *is*
with a common block (uhgg ... but remember that XManager, everybody's
favorite procedure, is implemented with a bunch of common blocks). The
issue is then one of locality and longevity -- we must ensure nothing
else alters our common block, and that, after the singleton's life is
over, the common block doesn't contain data. The latter is easy enough,
the former might be impossible.
I've been tinkering around with various ways to implement this type of
structure for a while... unfortunately, you cannot affect what gets
returned from Obj_New (more on this later). You could of course achieve
the same functionality with other, uglier ways.
An implementation is below. It defines a super-class, Singleton, which
is only meant to be inherited from. This class maintains a common block
variable "slist", in which are kept the various singleton objects. Only
one list is needed for as many different types of Singleton's you may
want, during a session, and, as you kill Singleton objects, the list is
cleaned.
*********** Singleton Abstract Class -- must be inherited
*******************************
pro Singleton::Cleanup
common Singleton, slist
if ptr_valid(slist) then begin
wh=where(NOT obj_isa(*slist, obj_class(self)),cnt)
if cnt ne n_elements(*slist) then $
if cnt eq 0 then ptr_free, slist else *slist=(*slist)[wh]
endif
end
pro Singleton::Init, Object=obj
common Singleton, slist
;; Add this type to our singleton list, if we need to.
if ptr_valid(slist) then begin
;; clean up the list .. removing any danglers
wh=where(obj_valid(*slist),cnt)
if cnt eq 0 then begin
ptr_free,slist
slist=ptr_new([self])
endif else begin
*slist=(*slist)[wh]
;; find us on the list
wh=where(obj_isa(*slist,obj_class(self)),cnt)
if cnt eq 0 then begin
*slist=[*slist,self] ;not yet on list -- add us
endif else begin ;we are already on the list !
abort=self
self=(*slist)[wh[0]]
obj_destroy,abort
endelse
endelse
endif else slist=ptr_new([self])
obj=self
return,1
end
pro Singleton__define
struct={Singleton, $
NULL:0b } ;I am forced to include something in
;this abstract class
end
************************************************************ ***************
And a User preferences object based on this ...
****************** UsrPref derived Class
**********************************
pro sUsrPref::PrintPrefs
print,'XSIZE: ',self.Prefs.xsize
print,'YSIZE: ',self.Prefs.ysize
print,'CTABL: ',self.Prefs.ctabl
end
pro sUsrPref::SetPrefs, XSIZE=xsize, YSIZE=ysize, CTABL=ctabl
if n_elements(xsize) ne 0 then self.Prefs.xsize=xsize
if n_elements(ysize) ne 0 then self.Prefs.ysize=ysize
if n_elements(ctabl) ne 0 then self.Prefs.ctabl=ctabl
end
pro sUsrPref__define
struct={USRPREF, $ ;A Structure to hold the preferences
XSIZE:0, $ ;x size of screen
YSIZE:0, $ ;y size of screen
CTABL:0} ;preferred color table
class={sUsrPref, $ ;the sUsrPref Class
INHERITS Singleton,$ ;make it a singleton
Prefs: {USRPREF}} ;pointer to user pref structure
end
************************************************************ ***************
The problems with this are:
1. You have to put some data in the Simpleton class, which really needs
no data (since it stores things in the common block). Required data
members, egad.
2. You are forced to call the derived class as
null=obj_new('sUsrPref',Object=sup), which will
always yield a valid object "sup" (the same one each time), but a valid
object "null" only once. If "self" were fully by-reference we could
skip this awkward keyword. Alas.
3. A heap variable is created and then destroyed when one of this
current type is already on the list. This is wasteful and slow.
Another possiblity to avoid this is to make a function to use instead of
obj_new, call it singleton()... something like:
function singleton, oType, _EXTRA=e
common Singleton, slist
if ptr_valid(slist) then begin
;; clean up the list .. removing any danglers
wh=where(obj_valid(*slist),cnt)
if cnt eq 0 then begin
ptr_free,slist
obj=obj_new(oType,_EXTRA=e)
slist=ptr_new([obj])
endif else begin
*slist=(*slist)[wh]
;; find us on the list
wh=where(obj_isa(*slist,oType),cnt)
if cnt eq 0 then begin
obj=obj_new(oType,_EXTRA=e)
*slist=[*slist,obj] ;not yet on list -- add us
endif else begin ;we are already on the list !
obj=(*slist)[wh[0]]
endelse
endelse
endif else begin
obj=obj_new(oType,_EXTRA=e)
slist=ptr_new([obj])
endelse
return,obj
end
This would make Singleton::Init unecessary (could remove it). Now you
would simply say:
sup=singleton('sUsrPref')
To get a new/current instance of the preferences object. However, this
is not a perfect replacement for obj_new() because only keyword
parameters (through inheritence) are permitted in the initted object.
Anyway, if something better comes to me, I'll let you know.
JD
____________________________________________________________ __________
J.D. Smith |*| WORK: (607) 255-5842
Cornell University Dept. of Astronomy |*| (607) 255-4083
206 Space Sciences Bldg. |*| FAX: (607) 255-5875
Ithaca, NY 14853 |*|
|
|
|
Re: IDL Object for creating a Singleton [message #11984 is a reply to message #11979] |
Mon, 22 June 1998 00:00  |
davidf
Messages: 2866 Registered: September 1996
|
Senior Member |
|
|
Phillip David (pdavid@earthling.net) writes:
> I've been doing a fair amount of reading on OO design and methodology. One of
> the books I've had pointed out to me was 'Design Patterns', which deals with
> OO algorithm design quite nicely. One of the patterns the authors describe is
> a Singleton -- A class for which only one instance exists. If the sole
> instance exists, the constructor function (object's init function in IDL) will
> return a reference to the existing object. If none exists, a new object is
> created and returned.
>
> This structure is particularly useful when there is no reason to have multiple
> objects around. I am trying to create a user preferences object that fits
> this criteria very nicely. I'd like to be able to store user preferences for
> screen size in an object. If that object exists, other screens can size
> themselves appropriately. The book gives sample C++ code that relies on class
> variables and class methods (i.e., not associated with a specific instance of
> the object, but with the object itself). IDL doesn't seem to have any way to
> do something similar. Does anyone know of a way to do this?
>
> My first attempt uses a common block called preferencesclassvars to store an
> object reference. This common block is called from a function that, while not
> part of the object, is stored in the same file. If the reference contained in
> the common block is not an object reference, then I know the object hasn't
> been initialized, and I set it to a new instance of the object. Otherwise, I
> return the object reference from the common block.
>
> Is there a better way to achieve the techniques of class variables and class methods?
Humm. Objects are turning out to be not only to be fun and powerful to
program, but synchronistic as well. This is the second time in two
days the _Design Patterns_ book (which I have not seen or read) was
mentioned to me and in an almost identical context. I was talking
to a friend about writing a "lurker" object, a singleton object like
Philip's screen preferences object, that lurks in the background
waiting for something to happen. In this case, I wanted to
update all the open windows when a color table was changed on a
24-bit display.
This wonderful idea suffers from other unsolvable (so far)
problems, but I did manage to get it started up. Like Philip,
I had to resort to a common block to initialize it properly.
Since common blocks are never my preferred solution, and since
this thing reeks of a hack, I'm also looking for something more
elegant. I'm afraid I am swimming in programming space that
is a little out of my depth. Perhaps someone like JD can throw
both Philip and me a life-line. :-)
Cheers,
David
--
David Fanning, Ph.D.
Fanning Software Consulting
E-Mail: davidf@dfanning.com
Phone: 970-221-0438
Coyote's Guide to IDL Programming: http://www.dfanning.com/
|
|
|
Re: IDL Object for creating a Singleton [message #12125 is a reply to message #11979] |
Mon, 22 June 1998 00:00  |
Phillip & Suzanne
Messages: 31 Registered: June 1998
|
Member |
|
|
J.D. Smith wrote:
>
> David Fanning wrote:
>>
>> Phillip David (pdavid@earthling.net) writes:
>>
>>> I've been doing a fair amount of reading on OO design and methodology. One of
>>> the books I've had pointed out to me was 'Design Patterns', which deals with
>>> OO algorithm design quite nicely. One of the patterns the authors describe is
>>> a Singleton -- A class for which only one instance exists. If the sole
>>> instance exists, the constructor function (object's init function in IDL) will
>>> return a reference to the existing object. If none exists, a new object is
>>> created and returned.
>>>
>>> This structure is particularly useful when there is no reason to have multiple
>>> objects around. I am trying to create a user preferences object that fits
>>> this criteria very nicely. I'd like to be able to store user preferences for
>>> screen size in an object. If that object exists, other screens can size
>>> themselves appropriately. The book gives sample C++ code that relies on class
>>> variables and class methods (i.e., not associated with a specific instance of
>>> the object, but with the object itself). IDL doesn't seem to have any way to
>>> do something similar. Does anyone know of a way to do this?
>>>
>>> Is there a better way to achieve the techniques of class variables and class methods?
>>
>> Humm. Objects are turning out to be not only to be fun and powerful to
>> program, but synchronistic as well. This is the second time in two
>> days the _Design Patterns_ book (which I have not seen or read) was
>> mentioned to me and in an almost identical context. I was talking
>> to a friend about writing a "lurker" object, a singleton object like
>> Phillip's screen preferences object, that lurks in the background
>> waiting for something to happen. In this case, I wanted to
>> update all the open windows when a color table was changed on a
>> 24-bit display.
>>
>> Cheers,
>
> I think you've both run head-on into the limits of IDL's OO
> implementation. The foremost among these is the lack of controllable
> access. All class members are equivalent to "protected" -- visible to a
> class plus any derived classes. Especially frustrating is the absence
> of public data members, which requires one to set up GetProperty and
> SetProperty methods which do *nothing* other than access otherwise
> "public" data.
I had already pretty much figured this out. I wanted to see if other people
could come up with a better implementation than my own for this, though. What
we really need in this instance is a "class variable" ("static" is the C++
term for it), which is a single variable available to all instances of a
class. This variable can be used for such information as a count of the
number of objects of this type currently instantiated or, in the case of a
singleton, the object reference to the only instance of the class.
> Protected class methods, although less frustrating, can limit the amount
> of cooperative programming possible for large projects (since no
> mechanism is provided to prevent accidental reliance on internal, and
> therefore potentially changing, class implementation).
>
> But one must keep in mind that RSI wanted to provide the basic OO
> functionality, not a totally robust OO environment. I think they did an
> O.K. job with this. Polymorphism is well-implemented since all methods
> are "virtual protected", and all objects (read object references) are
> the equivalent of object pointers in C++ ... there is no such concept as
> static typing of a method (just as there is no concept of static typing
> of a variable) in IDL. Encapsulation, though rigid, accomplishes the
> basics of the principle.
Thanks for that perspective.
> In any case, as to your specific problem, I haven't seen the book you
> reference, but I'd bet they achieve the "singleton" functionality with a
> static data member -- a variable which is shared by all instances of the
> defining class, but only initialized once. There is no comparable
> functionality in IDL. I believe the only way to implement this *is*
> with a common block (uhgg ... but remember that XManager, everybody's
> favorite procedure, is implemented with a bunch of common blocks).
<Nice singleton generator code omitted>
I hadn't thought of using a singleton generator, and was instead concerned
with the way to generate just a single instance of the singleton class. This
is a neat trick that I'll have to remember. Even the C++ example in "Design
Patterns" (a truly GREAT book on OO techniques!) had to make the constructor a
protected function, and create a class method (i.e., static) that checked
whether the static instance variable was initialized. If it was, the variable
was returned. If not, the constructor was called by the class variable, and
the instance reference updated.
In IDL, there are no such things as either class (static) variables or
methods. As a result, I've adopted the following way of handling these things:
For class variables, I define a common block <object>_static which has my
static variables inside of it. The only reference to this common block is
within my object or other routines found within the same file as my object.
Since IDL's object model only works on objects, it cannot use a static method
either. As a result, I simply add non-object routines into the same file as
my object is defined in, and use these as the structure I want. For this
particular project, I defined a file 'GetPreferences.pro', which had the
following structure:
----------- Start of sample source code -------------------
pro Preferences::GetSize
pro Preferences::Init, caller
if caller NE 'GetPreferences' then begin
ok = Widget_Message(/Error, $
'Preference objects can only be created by the GetPreferences routine.')
return, 0 ; failure
endif
;----------------------------------------------------------- -------------------
; I store my preferences in a file. If they're present, I read them in
when my
; preferences object gets initialized. I'm not including the code for this
; because it's not relevant. However, if the variables XSize and YSize are set
; to 0, I call the Device, Get_Screen_Size=sizes to get standard sizes.
;----------------------------------------------------------- -------------------
return, 1 ; success
end
pro Preferences__Define
struct = {PREFERENCES, XSize=0, YSize=0}
end
function GetPreferences
;----------------------------------------------------------- -------------------
; This is a 'static' function for the Preferences class. Notice that unless
; this function is called, the preferences object code is never even compiled
; unless it's compiled explicitly. This prevents the inadvertant call to a
; preferences object prior to invoking a GetPreferences routine. The check
; for the proper caller in the init function for the preferences object does
; the rest of the protection I can create. While it's not entirely foolproof
; this method does provide substantial protection.
;
; My main concern here is that this method is not technically a part of the
; class, but as JD points out, this may not be possible due to limitations
; of the object nature of IDL.
;----------------------------------------------------------- -------------------
common Preferences_Static, preferences
prefsSize = size(preferences)
if prefsSize(prefsSize(0)+1) EQ OBJECT then return, preferences
preferences = Obj_New('Preferences', 'GetPreferences')
return, preferences
end
---------- end of sample source code -------------
What do you think of this as a simpler approach when you just need a single Singleton?
I was hoping for something more elegant than common blocks, but I guess that's
what I'll continue to use. Thanks for your inputs. I'll try to continue
posting interesting OO ideas as I think of them.
By the way, David, what happened with the Direct Graphics as objects
techniques? I never saw more about them, but I haven't had proper newsgroup
access for a while.
Phillip
|
|
|