digitalmars.D - Proxy objects and controlling/monitoring access
- Bill Baxter (24/24) Aug 17 2007 This is something I've been thinking about lately.
- BCS (3/7) Aug 17 2007 why not do that? It could be done with mixin and __traits. It would be u...
- Reiner Pope (22/52) Aug 17 2007 I've written code which wraps a value in a class which derives from a
- Bill Baxter (3/12) Aug 17 2007 Cool. I'll give it a try when I get a chance and let you know what I fin...
- Reiner Pope (8/69) Aug 17 2007 After a bit of work, I've made a wrapper which will wrap a class and
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
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
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. --bbI'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
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
Reiner Pope wrote:Bill Baxter wrote: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. -- ReinerThis 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. --bbI'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