in reply to Subroutines with differing behaviour created from AUTOLOAD

The general approach seems perfectly reasonable to me. However I think you have a problem with the implementation: the methods you are injecting will be closures over the current $self, so it won't work if you have more than one object in the class.

I also think the code would be easier to read (and therefore maintain) if you create the method then goto it, rather than duplicating the functionality, something like (untested):

die "No such method $AUTOLOAD. Barfing\n" unless exists $self->{$field}; if ($self->{$field}{editable}) { *$AUTOLOAD = sub { my $self = shift; $self->{$field}{value} = shift if @_; $self->{$field}{value}; }; } else { *$AUTOLOAD = sub { my $self = shift; die "Can't modify readonly field '$field'" if @_; $self->{$field}{value}; }; } goto &$AUTOLOAD;

Hugo

Replies are listed 'Best First'.
Re^2: Subroutines with differing behaviour created from AUTOLOAD
by dragonchild (Archbishop) on Jun 08, 2004 at 15:12 UTC
    Untested is right. That method will DEFINITELY work with more than one object in the class. I should know - I use it in production. You are NOT "injecting"(?) closures over the current $self. Test it before you make pronouncements like that.

    However, the method will have issues for other reasons. The code implies that two difference instances might have the same field be editable or not at the instance level. If that's the case, then the first instance to access the field will set its editable-ness for all instances of the class.

    There are two solutions. If each instance needs to maintain editability, then you have to go to the OP's solution, which is to have AUTOLOAD evaluate everything on a case by case basis. (This can still be gotten around, but you'd have to attach the closures to the instance and have the class methods dispatch to the instance's method. Too much work!)

    If the fields are editable based on the class, then hoist the editability to the class level and deal with it there.

    my $class = ref($self); my %editable = do { no strict 'refs'; %{"${class}::__EDITABLE__"}; }; die "No such method $AUTOLOAD. Barfing\n" unless exists $editable{$field}; if ($editable{$field}) { *$AUTOLOAD = sub { my $self = shift; $self->{$field}{value} = shift if @_; $self->{$field}{value}; }; } else { *$AUTOLOAD = sub { my $self = shift; die "Can't modify readonly field '$field'" if @_; $self->{$field}{value}; }; } goto &$AUTOLOAD;

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      Untested is right. That method will DEFINITELY work with more than one object in the class. I should know - I use it in production. You are NOT "injecting"(?) closures over the current $self. Test it before you make pronouncements like that.

      With the original poster's code, this test:

      my $test = Foo->new(); my $test2 = Foo->new(); $test->read_write("water"); $test2->read_write("wine"); print "test: should be water, is ", $test->read_write, "\n"; print "test2: should be wine, is ", $test2->read_write, "\n";
      gives me:
      test: should be water, is wine test2: should be wine, is wine

      I'm confused as to what you're saying will definitely work - the OP's code has an AUTOLOAD sub which defines a lexical $self, in which he defines a couple of anonymous subs which refer to that $self. Nothing that can't be fixed by adding a my $self into the subs, as I did in my code and as you've done in yours.

      Hugo

        Nothing that can't be fixed by adding a my $self into the subs
        After a couple of /msg's with dragonchild, I realise this is what I should have done in my created subs. To do otherwise creates the problem you originally told me about.

        davis
        It's not easy to juggle a pregnant wife and a troubled child, but somehow I managed to fit in eight hours of TV a day.
        Ahhh. You were referring to a problem with the OP's code, not a problem inherent with the algorithm. Your code didn't have the problem you mentioned, but you didn't say that. Hence why I thought you were referring to a problem that your code would still have.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

        I shouldn't have to say this, but any code, unless otherwise stated, is untested

      Can you explain what
      my %editable = do { no strict 'refs'; %{"${class}::__EDITABLE__"}; };
      does please? It looks to me like it's using a filehandle to populate a hash of the attributes that should be editable. Can you give an example of how to use this?
      Cheers

      davis
      It's not easy to juggle a pregnant wife and a troubled child, but somehow I managed to fit in eight hours of TV a day.
        Filehandle?? Nonono. Read up on do. Basically, it's a way of wrapping a block around an assignment, kinda like what I've done there. The idea is to directly access the symbol table using a soft (or symbolic) reference to find the %__EDITABLE__ hash in the class of the object being used. This way, we don't have to hardcode the classname in.

        Better yet would be to provide a method (called editable()?) which would return those values. This would play nicer with inheritance. Of course, the best solution is to have something that walks the @ISA tree, but that gets more complicated.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

        I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re^2: Subroutines with differing behaviour created from AUTOLOAD
by davis (Vicar) on Jun 08, 2004 at 14:45 UTC
    the methods you are injecting will be closures over the current $self, so it won't work if you have more than one object in the class
    Wow, apparently not. Although I realised the subs were closures (by keeping the value $field), I never realised that this would break for more than one object instance.
    Does this mean that I should remove the assignment to the symbol table and live with the performance penalty?

    davis
    It's not easy to juggle a pregnant wife and a troubled child, but somehow I managed to fit in eight hours of TV a day.
      This is a case when eval saves the day: (warning untested)
      die "No such method $AUTOLOAD. Barfing\n" unless exists $self->{$field}; if ($self->{$field}{editable}) { *$AUTOLOAD = eval <<ENDSUB; sub { my \$self = shift; \$self->{$field}{value} = shift if \@_; \$self->{$field}{value}; } ENDSUB } else { *$AUTOLOAD = eval <<ENDSUB2; sub { my \$self = shift; die "Can't modify readonly field '$field'" if \@_; \$self->{$field}{value}; } ENDSUB2 } goto &$AUTOLOAD;

      --
      I'm Not Just Another Perl Hacker

        That works (and is pretty damn cool, to boot), but doesn't it also negate the symbol table-insertion techniques? The eval'd code will have to be parsed anew each time the sub is called.

        davis
        It's not easy to juggle a pregnant wife and a troubled child, but somehow I managed to fit in eight hours of TV a day.

      No, it means only that you should declare my $self in the subs you create, as in my example - without such a declaration, the $self in the anonymous subs refers to the closest enclosing declaration (the my $self of the AUTOLOAD routine), and that is what creates the unwanted closure.

      You might prefer to further reduce the potential for confusion by using a different variable name for the two: perhaps by renaming the outer $self to $object or somesuch.

      Hugo