www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proxy objects and controlling/monitoring access

reply Bill Baxter <dnewsgroup billbaxter.com> writes:
This is something I've been thinking about lately.
Here's something I think is maybe a sort of language challenge akin to 
Andrei's "identity" template.  It's something that if you can do, opens 
up a lot of possibilities, even though it may not be that interesting by 
itself.

Ok, so the challenge is, can you make a wrapper around a value that 
behaves identically to the value BUT also allows you to monitor each 
access?  The uses for this are many.  In particular observer patterns 
and the like often involve getting notifications when things change.

One thing making me thing about this is the enthough.traits framework in 
Python that I've been looking at lately.  It's a very powerful paradigm 
for linking components together.  Basically when you use traits, instead 
of creating class memebers in the normal way you create them using 
something akin to 'my_int = new traits.Int'.  From there my_int 
basically looks exactly like a regular int to all users, the twist being 
that you can easily get a notification every time my_int changes.

So can we do this in D?  I guess you might call this "identity with 
notifications"

We can certainly do a lot of it using operator overloads.  But we can't 
do everything.  For instance, if you want to wrap a struct there's no 
way to emulate getting and setting of the struct's members without 
writing a specific wrapper that adds property get/set methods for each 
member of the wrapped struct.


--bb
Aug 17 2007
next sibling parent BCS <ao pathlink.com> writes:
Reply to Bill,

 For instance, if you want to wrap a struct
 there's no way to emulate getting and setting of the struct's members
 without writing a specific wrapper that adds property get/set methods
 for each member of the wrapped struct.
why not do that? It could be done with mixin and __traits. It would be ugly though. (Man I want tuple foreach outside function scope!)
Aug 17 2007
prev sibling parent reply Reiner Pope <some address.com> writes:
Bill Baxter wrote:
 This is something I've been thinking about lately.
 Here's something I think is maybe a sort of language challenge akin to 
 Andrei's "identity" template.  It's something that if you can do, opens 
 up a lot of possibilities, even though it may not be that interesting by 
 itself.
 
 Ok, so the challenge is, can you make a wrapper around a value that 
 behaves identically to the value BUT also allows you to monitor each 
 access?  The uses for this are many.  In particular observer patterns 
 and the like often involve getting notifications when things change.
 
 One thing making me thing about this is the enthough.traits framework in 
 Python that I've been looking at lately.  It's a very powerful paradigm 
 for linking components together.  Basically when you use traits, instead 
 of creating class memebers in the normal way you create them using 
 something akin to 'my_int = new traits.Int'.  From there my_int 
 basically looks exactly like a regular int to all users, the twist being 
 that you can easily get a notification every time my_int changes.
 
 So can we do this in D?  I guess you might call this "identity with 
 notifications"
 
 We can certainly do a lot of it using operator overloads.  But we can't 
 do everything.  For instance, if you want to wrap a struct there's no 
 way to emulate getting and setting of the struct's members without 
 writing a specific wrapper that adds property get/set methods for each 
 member of the wrapped struct.
 
 
 --bb
I've written code which wraps a value in a class which derives from a given interface. The interface is implemented by calling the value's fields. See the unit test of the attached mixininterface.d for examples. In fact, the code isn't so messy, but it uses my own collection of CTFE utils in my_metastrings. I don't imagine it would be too hard to adapt my code to generate the wrapper you want. There are two behavioural differences, though: 1. The generated class will have reference semantics, whether the backing type is a value or a reference. This is partly a consequence of wrapping it in an interface, but if I was trying to create the wrapper you wanted, it would still be a problem. One solution would simply be to check whether the type is a value or a reference type. If it's a value type, implement the wrapper as a struct, if it's a reference, implement the wrapper as a class. The alternative (but currently impossible) would be to overload the class's opAssign for itself, emulating value semantics. 2. Casting doesn't work properly. You don't retain the implementation's position in the inheritance tree, so you can't cast back to it, etc. -- Reiner
Aug 17 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Reiner Pope wrote:
 Bill Baxter wrote:
 I've written code which wraps a value in a class which derives from a 
 given interface. The interface is implemented by calling the value's 
 fields.  See the unit test of the attached mixininterface.d for 
 examples. In fact, the code isn't so messy, but it uses my own 
 collection of CTFE utils in my_metastrings.
 
 I don't imagine it would be too hard to adapt my code to generate the 
 wrapper you want.
Cool. I'll give it a try when I get a chance and let you know what I find. --bb
Aug 17 2007
prev sibling parent Reiner Pope <some address.com> writes:
Reiner Pope wrote:
 Bill Baxter wrote:
 This is something I've been thinking about lately.
 Here's something I think is maybe a sort of language challenge akin to 
 Andrei's "identity" template.  It's something that if you can do, 
 opens up a lot of possibilities, even though it may not be that 
 interesting by itself.

 Ok, so the challenge is, can you make a wrapper around a value that 
 behaves identically to the value BUT also allows you to monitor each 
 access?  The uses for this are many.  In particular observer patterns 
 and the like often involve getting notifications when things change.

 One thing making me thing about this is the enthough.traits framework 
 in Python that I've been looking at lately.  It's a very powerful 
 paradigm for linking components together.  Basically when you use 
 traits, instead of creating class memebers in the normal way you 
 create them using something akin to 'my_int = new traits.Int'.  From 
 there my_int basically looks exactly like a regular int to all users, 
 the twist being that you can easily get a notification every time 
 my_int changes.

 So can we do this in D?  I guess you might call this "identity with 
 notifications"

 We can certainly do a lot of it using operator overloads.  But we 
 can't do everything.  For instance, if you want to wrap a struct 
 there's no way to emulate getting and setting of the struct's members 
 without writing a specific wrapper that adds property get/set methods 
 for each member of the wrapped struct.


 --bb
I've written code which wraps a value in a class which derives from a given interface. The interface is implemented by calling the value's fields. See the unit test of the attached mixininterface.d for examples. In fact, the code isn't so messy, but it uses my own collection of CTFE utils in my_metastrings. I don't imagine it would be too hard to adapt my code to generate the wrapper you want. There are two behavioural differences, though: 1. The generated class will have reference semantics, whether the backing type is a value or a reference. This is partly a consequence of wrapping it in an interface, but if I was trying to create the wrapper you wanted, it would still be a problem. One solution would simply be to check whether the type is a value or a reference type. If it's a value type, implement the wrapper as a struct, if it's a reference, implement the wrapper as a class. The alternative (but currently impossible) would be to overload the class's opAssign for itself, emulating value semantics. 2. Casting doesn't work properly. You don't retain the implementation's position in the inheritance tree, so you can't cast back to it, etc. -- Reiner
After a bit of work, I've made a wrapper which will wrap a class and allow you to attach listeners for when the methods are called. But I'm not sure how to list the non-virtual overloads of a member; there doesn't seem to be anything in __traits to do that. Code attached. I've attached a new version of my_metastrings.d which has a workaround to avoid listing 'this' as a class member. -- Reiner
Aug 17 2007