Design pattern for wrapper/proxy objects?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Design pattern for wrapper/proxy objects?

Luke Gorrie
Howdy,

I have some Smalltalk objects that represent external data and I am looking for a neat way to access them.

The data that I am dealing with is C binary objects described by DWARF schemas. So I will load a type declaration like this:

struct outer {
  struct inner {
    int a, b;
  };
};

and then I will want to be able to write convenient expressions like:

aStruct outer inner a + aStruct outer inner b

I have implemented this already but I am not sure that I took the right approach. I have implemented these accessors like #outer #inner #a #b via a messageNotUnderstood: method that looks into the object, so that 'aStruct outer' is equivalent to 'aStruct field: #outer'. My concern is that the names of the C struct fields can collide with existing Smalltalk methods and then I will get totally unexpected results.

Is there a canonical solution to this problem?

The first idea I have is to add a bit of syntatic sugar, like to write 'aStruct _outer _inner _a' where the leading underscore avoids method name collisions and is then stripped off in messageNotUnderstood. That doesn't seem especially elegant though.

I can't quite bring myself to write verbose things like

((aStruct child: #outer) child: #inner) child: #a


Reply | Threaded
Open this post in threaded view
|

Re: Design pattern for wrapper/proxy objects?

Ben Coman
On 4 December 2017 at 00:10, Luke Gorrie <[hidden email]> wrote:

> Howdy,
>
> I have some Smalltalk objects that represent external data and I am looking
> for a neat way to access them.
>
> The data that I am dealing with is C binary objects described by DWARF
> schemas. So I will load a type declaration like this:
>
> struct outer {
>   struct inner {
>     int a, b;
>   };
> };
>
> and then I will want to be able to write convenient expressions like:
>
> aStruct outer inner a + aStruct outer inner b
>
> I have implemented this already but I am not sure that I took the right
> approach.

Seems like a reasonable approach, except difficulties with the
broadness of your DSL domain (as you've found.)


> I have implemented these accessors like #outer #inner #a #b via a
> messageNotUnderstood: method that looks into the object, so that 'aStruct
> outer' is equivalent to 'aStruct field: #outer'. My concern is that the
> names of the C struct fields can collide with existing Smalltalk methods and
> then I will get totally unexpected results.
>
> Is there a canonical solution to this problem?
>
> The first idea I have is to add a bit of syntatic sugar, like to write
> 'aStruct _outer _inner _a' where the leading underscore avoids method name
> collisions and is then stripped off in messageNotUnderstood. That doesn't
> seem especially elegant though.
>
> I can't quite bring myself to write verbose things like
>
> ((aStruct child: #outer) child: #inner) child: #a
>
>

An easy improvement might be to subclass your DwarfStruct from
ProtoObject rather than Object
to significantly reduce the surface area of name clashes.
* https://github.com/pharo-project/pharo/blob/development/src/Kernel/ProtoObject.class.st

But I've not worked much with ProtoObject so I'm not sure what
difficulties that might bring.
In particular, can anyone comment on extra requirements to support
object display in GT Inspector?


A naming clash with the remaining ProtoObject methods seems extremely unlikely,
and the easiest path would be to just guard against loading such structs.
    DwarfStruct>>loadStructure
        ...
        nameClashes := ProtoObject methodNames.
        self fieldnames do: [ :f | (nameClashes includes: f)
                 ifTrue: [ self error: 'Seriously!? Your trying awful
hard to break me' ] ]


Just for the fun of it(!?) you might even try reducing that surface
area by renaming that just in your personal Images by renaming
ProtoObject methods.
(Actually thats any interesting experiment I'll have to try later when
I've got Pharo in front of me.)


The only other thing I can think of is, presuming you've got a single
class (e.g. DwarfStruct) holding the structure as data.
for each loaded structure, generate "DwarfStruct subclass: CStruct1"
and CStruct1>>fieldname
so that method lookup stops at the first class and doesn't run all the
way up the class hierarchy.
But that would still be susceptible to breaking the system by
overloading messages critical to system operation.

cheers -ben

Reply | Threaded
Open this post in threaded view
|

Re: Design pattern for wrapper/proxy objects?

Henrik Sperre Johansen
In reply to this post by Luke Gorrie
For proxies/wrappers, you might want to look into how the Ghost framework
interrupts sends.
(I don't know the exact details, but I'd guess step 1 is subclassing
ProtoObject, rather than Object)

For your specific case, isn't there already a *Struct class in the image you
can subclass, create a class method containing the fields definition, and
then have accessors autogenerated, rather than using doesNotUnderstand:?
Usually, that would make the code easier to browse/debug later on, doesn't
confuse the syntax highlighter/code critic in uses of an otherwise undefined
field, etc.

Cheers,
Henry



--
Sent from: http://forum.world.st/Pharo-Smalltalk-Developers-f1294837.html

Reply | Threaded
Open this post in threaded view
|

Re: Design pattern for wrapper/proxy objects?

Luke Gorrie
Thanks Ben & Henrik for the ideas!

I will scratch my head a bit and then try to do something sensible. :-)


Reply | Threaded
Open this post in threaded view
|

Re: Design pattern for wrapper/proxy objects?

Mariano Martinez Peck
Hi Luke,

Not only for the details of a particular proxy implementation like Ghost, but also because of all the discussions around it,  I think this journal paper [1] we wrote may be of help to you.

Cheers,



On Mon, Dec 4, 2017 at 5:51 AM, Luke Gorrie <[hidden email]> wrote:
Thanks Ben & Henrik for the ideas!

I will scratch my head a bit and then try to do something sensible. :-)





--