Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re: Class::InsideOut - yet another riff on inside out objects.

by Aristotle (Chancellor)
on Dec 19, 2002 at 16:15 UTC ( [id://221145]=note: print w/replies, xml ) Need Help??


in reply to Class::InsideOut - yet another riff on inside out objects.

In general, I like your approach - a lot.

One thing that annoyed me is the $self->self meme. Confusing, IMO, and not efficient either. I toyed around with the idea of wrapping subs in some way, but haven't come up with any really consistent and watertight semantics. :-/ The only possibility would be to tie all field hashes to a class that autocasts any refs used as keys into their refaddr before using them but that doesn't perform any better. Overloading the stringification on the reference might help, but will neither perform better nor work reliably if someone else overloads the same operation. So there really seems to be no other way than to remind everyone to use $self->self everywhere. Though I'd probably call that $self->_id instead. Or maybe an attribute Self? Not sure yet.. gonna have to look into that.

That aside, here's my take on the base class - minus refaddr cause it doesn't work on 5.6.1. What I do is quite simple: store the hashref to the pad :-). Then all that AUTOLOAD has to do is trawl through the pad hashrefs and look for a matching attribute.

#!/usr/bin/perl use strict; use warnings; package Class::InsideOut; use NEXT; use Attribute::Handlers; use PadWalker qw(peek_my); use Data::Dumper; use vars qw($AUTOLOAD); my (@Field, %Pad); sub Field : ATTR(HASH) { my ($class, $symbol, $hash) = @_; push @Field, $hash; my $pad = peek_my(3); $Pad{$class}->{$pad} = peek_my(3); } sub AUTOLOAD { my $self = $_[0]; my ($class, $field) = $AUTOLOAD =~ /^(.*)::(.*)$/; $field = "%".$field; my @field = grep exists $_->{$field}, values %{$Pad{$class}}; if(@field) { $field = $field[0]; no strict 'refs'; *{$AUTOLOAD} = sub { my $self = shift; @_ ? $field->{$self} = shift : $field->{$self}; }; goto &{$AUTOLOAD}; } else { warn "\@field is empty"; return $self->NEXT::AUTOLOAD; } } sub DESTROY { my $self = $_[0]; delete $_->{$self} for @Field; $self->NEXT::DESTROY() } 1;
And some test code:
package Foo::Bar; use base qw(Class::InsideOut); use Data::Dumper; my (%foo, %bar, %baz): Field; package main; my $x = bless [], 'Foo::Bar'; $x->foo("bar!\n"); print $x->foo(), "\n"; __END__ bar!
I don't know if it's watertight, though. In particular, how well will it work if I call an accessor for a superclasses' field on a subclass?

Makeshifts last the longest.

Replies are listed 'Best First'.
Re^2: Class::InsideOut - yet another riff on inside out objects.
by adrianh (Chancellor) on Dec 19, 2002 at 17:16 UTC
    In general, I like your approach - a lot.
    <blush />
    One thing that annoyed me is the $self->self meme.

    Me to, but like you I cannot see a way round it. I've just renamed it self_id in my local version.

    That aside, here's my take on the base class - minus refaddr cause it doesn't work on 5.6.1.

    Are you sure? It's basically the same implementation as the one here.

    What I do is quite simple: store the hashref to the pad :-). Then all that AUTOLOAD has to do is trawl through the pad hashrefs and look for a matching attribute.

    Falls over completely on 5.8.0. Probably because of this :-)

    I like your use of an array to store the class hashes in. Makes the logic easier to understand. Might be a problem in the context of serialisation - since the position of the hash depends on the load order of the classes used by your application. You could always store a class->position mapping I guess.

    I don't know if it's watertight, though. In particular, how well will it work if I call an accessor for a superclasses' field on a subclass?

    There's the begining of a test suite in the version at http://www.quietstars.com/perl/ if you're interested.

      Falls over completely on 5.8.0.

      :((

      I did not think about serialization at all when I moved to an array - there was a very simple reason for that - my first step was, since you're not using the $class in your while in DESTROY, to make that

      delete $_->{id} for map @$_, values %Values; At that point I blinked, looked hard at the entire source I had in front of me, which didn't include ::YAML, and saw there was nothing to ever make use of the key - obviously an oversight. I haven't looked at the ::YAML code at all yet (not the least reason being I haven't dealt with at YAML at all yet either) so I'm not sure if that will work, but a simple way out might be an extra hash keyed on the refaddr of the fieldhashref and storing arrays of classnames. I'm just pulling straws out of thin air here though.

      Makeshifts last the longest.

Re: Re: Class::InsideOut - yet another riff on inside out objects.
by John M. Dlugosz (Monsignor) on Dec 19, 2002 at 19:52 UTC
    Hmm, I think we are getting away from the real issue. Finding a way to hook up lexicals with attributes is not the real point, though interesting in itself. The real point is to make a succinct way to declare instance data.

    So, don't use an attributed declaration. Instead, use a syntax like: field ('name', options); that will create the underlying hash itself, rather than the caller making one.

    The underlying hash can either be "hands off", or there can be a way to get to it (return value from that call?) if you really want to support it.

    { # extra scope $xx= field qw/xx private/; sub something { # I use that instance data internally. # ... ... $$xx{$id} ... }
    Being private, no access method is autogenerated, and the returned ref is the only way to get to it.

    —John

      Well, the point is twofold:
      1. Most importantly, we need a way for a generic DESTROY to work.
      2. Ideally we wouldn't have to name the the field more than once and only once.
      The attribute semantics seem to offer the most succint possible syntax to reach both of those goals - provided it can at all be made to work, of course.

      Makeshifts last the longest.

        The generic destroy would work fine, since the class would remember all the fields it generated using this syntax just as well as it could using any other syntax.

        I agree that this still names the field twice (public name optional, internal ref optional) when most of the time you'd be happy to use the same name in both places.

      This would work, but has a couple of minor issues:

      • We've added another layer of indirection. Means that direct access is a little slower.
      • We have to mention the "name" of the object attribute twice (the xx in $$xx and qw/xx private/.
      • It's more work if you don't want the accessors.

      If we want to name an object attribute explicitly we could do it with a modified :Field attribute. For example, something like this wouldn't be hard to implement.

      my %foo : Field; # no accessor my %foo : Field(as foo); # create accessor named foo

      While easy to implement, it still has the duplication of names... which offends :-)

        Direct access is slower: Dereferencing the hash ref is not a big deal. It's only taking a reference that's slow, for some reason.

        Naming the object twice: good point, that's no better than putting the name in the attribute's text.

        Not generating an accessor: how do you do it now? I suppose that if you write your own accessor method it will overwrite the generated one, but then you have to make sure it happens in that order, and you have to take pains to zap the ones you don't want.

        I think the 5.x attribute mechanism should be extended to get the source name and ref, not just (sometimes) a glob. As is now, attributes are pretty pointless on lexicals because it doesn't provide a way to associate the attribute with the thing it's attached to!

        —John

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://221145]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2024-04-19 10:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found