Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

new objectclass question

0 views
Skip to first unread message

Jonathan L Cunningham

unread,
Mar 19, 2008, 10:04:52 AM3/19/08
to
Hi, can anyone help with the following?

I've boiled the problem down as much as I can, into a simple test
example (see below). It should be fairly clear what I want to do.

Ideally, I would like to avoid changing existing code which uses the
base class - I could do it by renaming one of the slots in the base
class, and defining a method with the old slot name, but this would
mean an extra method call every time the slot is accessed.

There might be a better way (and it may be lurking in the documentation,
but I haven't found it).

Test prog follows. (Compile everything up to the first comment, then go
through reading each comment and evaluating the code one line at a
time.) Suggestions/discussion welcome.

Jonathan


;;; objtest.p 2008-03-19
;;; version: 2008Mar19
;;; test program for using objectclass

uses objectclass;

define encode( list );
[encoding ^list]=>
10*list(1) + list(2);
enddefine;

define :class base;
slot value;
enddefine;

define :method get_encoded_value( obj:base ) -> coded_value;
encode( value( obj )) -> coded_value;
enddefine;

define :class derived is base;
slot cached_coded_value;
enddefine;

define :method get_encoded_value ( obj:derived ) -> coded_value;
cached_coded_value( obj ) -> coded_value;
unless coded_value then
encode( value( obj )) -> coded_value;
coded_value -> cached_coded_value( obj );
endunless;
enddefine;

;;; let's make an object
consbase([2 3]) -> an_object;
an_object =>

;;; we can get its encoded value
get_encoded_value(an_object)=>

;;; we can change its value
[4 5] -> value(an_object);
an_object =>
value(an_object)=>

;;; and get the new encoded value
get_encoded_value(an_object)=>

;;; let's make an object which caches the encoded value
consderived([2 3], false) -> an_object;
an_object =>

;;; get its encoded value
get_encoded_value(an_object)=>
;;; and the value is cached
an_object=>
;;; so we can fetch it again without re-encoding it
get_encoded_value(an_object)=>

;;; now we change its value
[4 5] -> value(an_object);

;;; Ooops! the cached coded value is incorrect!
an_object =>

;;; Neither of the following work:

;;; First attempt, this causes a compile time error
/*
define :wrapper updaterof value( newvalue, obj:derived, upd_proc );
false -> cached_coded_value( obj );
upd_proc( newvalue, obj );
enddefine;
*/

;;; Second attempt, this compiles, ok...
define :method updaterof value( newvalue, obj:derived );
false -> cached_coded_value( obj );
call_next_method( newvalue, obj );
enddefine;

;;; ... but fails at run-time.

[4 5] -> value(an_object);

;;; It's no good defining a wrapper on the base class
define :wrapper updaterof value( newvalue, obj:base, upd_proc );
false -> cached_coded_value( obj );
upd_proc( newvalue, obj );
enddefine;

;;; because although it works
[4 5] -> value(an_object);
an_object=>

;;; it fails on ordinary, uncached, objects:
consbase([2 3]) -> an_object;
[4 5] -> value(an_object);

;;; Any ideas?

Waldek Hebisch

unread,
Mar 19, 2008, 9:45:10 PM3/19/08
to
Jonathan L Cunningham <sp...@sofluc.co.uk.invalid> wrote:
> Hi, can anyone help with the following?
>
> I've boiled the problem down as much as I can, into a simple test
> example (see below). It should be fairly clear what I want to do.
>
> Ideally, I would like to avoid changing existing code which uses the
> base class - I could do it by renaming one of the slots in the base
> class, and defining a method with the old slot name, but this would
> mean an extra method call every time the slot is accessed.
>

You have a base class string values and a derived class which should
cache results of computation. AFAICS you want to redefine updater of
a slot in a derived class, so that you can invalidate cached value.

> ;;; It's no good defining a wrapper on the base class
> define :wrapper updaterof value( newvalue, obj:base, upd_proc );
> false -> cached_coded_value( obj );
> upd_proc( newvalue, obj );
> enddefine;
>
> ;;; because although it works
> [4 5] -> value(an_object);
> an_object=>
>
> ;;; it fails on ordinary, uncached, objects:
> consbase([2 3]) -> an_object;
> [4 5] -> value(an_object);
>
> ;;; Any ideas?

The following works on all objects:

define :wrapper updaterof value( newvalue, obj:base, upd_proc );

if isderived(obj) then


false -> cached_coded_value( obj );

endif;


upd_proc( newvalue, obj );
enddefine;

It has two drawbacks. One is that we run the updater not only on
updates to derived class, but also on updates to the base class.
The second is that the 'isderived(obj)' test duplicates work done
by dispatch code.

--
Waldek Hebisch
heb...@math.uni.wroc.pl

Jonathan L Cunningham

unread,
Mar 20, 2008, 7:27:12 AM3/20/08
to
Waldek Hebisch <heb...@math.uni.wroc.pl> wrote:

> Jonathan L Cunningham <sp...@sofluc.co.uk.invalid> wrote:
> > Hi, can anyone help with the following?
> >
> > I've boiled the problem down as much as I can, into a simple test
> > example (see below). It should be fairly clear what I want to do.
> >

> You have a base class string values and a derived class which should


> cache results of computation. AFAICS you want to redefine updater of
> a slot in a derived class, so that you can invalidate cached value.

Yes.

> > ;;; It's no good defining a wrapper on the base class
> > define :wrapper updaterof value( newvalue, obj:base, upd_proc );
> > false -> cached_coded_value( obj );
> > upd_proc( newvalue, obj );
> > enddefine;
> >
> > ;;; because although it works
> > [4 5] -> value(an_object);
> > an_object=>
> >
> > ;;; it fails on ordinary, uncached, objects:
> > consbase([2 3]) -> an_object;
> > [4 5] -> value(an_object);
> >
> > ;;; Any ideas?
>
> The following works on all objects:
>
> define :wrapper updaterof value( newvalue, obj:base, upd_proc );
> if isderived(obj) then
> false -> cached_coded_value( obj );
> endif;
> upd_proc( newvalue, obj );
> enddefine;
>
> It has two drawbacks. One is that we run the updater not only on
> updates to derived class, but also on updates to the base class.
> The second is that the 'isderived(obj)' test duplicates work done
> by dispatch code.

Thanks. I agree that this is a good solution - with minor drawbacks.

I don't understand why the call_next_method() approach doesn't work, but
I have other things to think about! :-)

> > Ideally, I would like to avoid changing existing code which uses the
> > base class - I could do it by renaming one of the slots in the base
> > class, and defining a method with the old slot name, but this would
> > mean an extra method call every time the slot is accessed.

In fact, this is what I did, because I decided I wanted to do type
conversions on some stored values. Instead of

define :class base;
slot value;
enddefine;

I now do something like:

define :class base;
slot raw_value;
enddefine;

define :method value( obj:base );
raw_value( obj );
enddefine;

define :method updaterof value( newvalue, obj:base );
if some_test( newvalue ) then
convert( newvalue )
else
newvalue
endif -> raw_value( obj );
enddefine;

And then I use the call_next_method() technique in the derived class, to
cache the value. (The real code is more complex.)

The drawback here is one extra method call on access or update, but it's
possible to call raw_value() directly when I know it is safe. (I
prefer not to do that unless efficiency is absolutely important: that
kind of source-code "optimisation" is a good source of bugs!)

Thanks again for your reply.

Jonathan

0 new messages