Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

AUTOLOAD not following @ISA as expected [solved]

by chibiryuu (Beadle)
on Aug 02, 2005 at 20:42 UTC ( #480295=perlquestion: print w/replies, xml ) Need Help??
chibiryuu has asked for the wisdom of the Perl Monks concerning the following question:

I tried to make a simple module:

# Autoattr.pm package Autoattr; our $DEBUG; sub new($) { $DEBUG and printf STDERR "%s::new(%s)\n", __PACKAGE__, join ', ', map "'$_'", @_; ref $_[0] ? bless {%{$_[0]}}, ref $_[0] : bless {}, $_[0]; } sub DESTROY { $DEBUG and printf STDERR "%s::DESTROY(%s)\n", __PACKAGE__, join ', ', map "'$_'", @_; return; } sub AUTOLOAD { no strict qw(refs); our $AUTOLOAD; $DEBUG and printf STDERR "%s::AUTOLOAD=%s(%s)\n", __PACKAGE__, $AUTOLOAD, join ', ', map "'$_'", @_; if ($AUTOLOAD =~ /:get_([^:]*)$/) { my $attr = $1; *{$AUTOLOAD} = sub {$_[0]->{$attr}}; } elsif ($AUTOLOAD =~ /:set_([^:]*)$/) { my $attr = $1; *{$AUTOLOAD} = sub {$_[0]->{$attr} = $_[1]; $_[0]}; } else { (my $get = $AUTOLOAD) =~ s/([^:]*)$/get_$1/; (my $set = $AUTOLOAD) =~ s/([^:]*)$/set_$1/; *{$AUTOLOAD} = sub {goto \&{$#_ ? $set : $get}}; } goto \&$AUTOLOAD; } 1;
And use it thusly:
#!/usr/bin/perl -w package Foo; use base qw(Autoattr); sub get_foo {$_[0]->{foo} .= 'foo'} sub set_bar {$_[0]->{bar} = 'bar' x $_[1]; $_[0]} package Bar; use base qw(Foo); package main; BEGIN {$Autoattr::DEBUG = 1} $_ = Bar->new; printf "\n>>> %s, %s, %s\n\n", $_->foo, $_->foo, $_->foo; printf "\n>>> %s, %s, %s\n\n", do {$_->bar(3); $_->bar}, do {$_->bar(2); $_->bar}, do {$_->bar(1); $_->bar};

The idea was to somewhat emulate def foo and def bar= from Ruby.

If I use $_ = Foo->new, everything works as expected.

Autoattr::new('Foo') + Autoattr::AUTOLOAD=Foo::foo('Foo=HASH(0x225140)') + + >>> foo, foofoo, foofoofoo + + Autoattr::AUTOLOAD=Foo::bar('Foo=HASH(0x225140)', '3') + Autoattr::AUTOLOAD=Foo::get_bar('Foo=HASH(0x225140)') + >>> barbarbar, barbar, bar + + Autoattr::DESTROY('Foo=HASH(0x225140)') +

However, with $_ = Bar->new as above, it no longer works.

Autoattr::new('Bar') + Autoattr::AUTOLOAD=Bar::foo('Bar=HASH(0x225164)') + Autoattr::AUTOLOAD=Bar::get_foo('Bar=HASH(0x225164)') + Use of uninitialized value in printf at C:\dev\perl\test.pl line 32. + Use of uninitialized value in printf at C:\dev\perl\test.pl line 32. + Use of uninitialized value in printf at C:\dev\perl\test.pl line 32. + + >>> , , + + Autoattr::AUTOLOAD=Bar::bar('Bar=HASH(0x225164)', '3') + Autoattr::AUTOLOAD=Bar::set_bar('Bar=HASH(0x225164)', '3') + Autoattr::AUTOLOAD=Bar::get_bar('Bar=HASH(0x225164)') + >>> 3, 2, 1 + + Autoattr::DESTROY('Bar=HASH(0x225164)') +

To me, it seems like bouncing back out of &AUTOLOAD doesn't return to into traversing @ISA, which is sad. :-(

Am I misinterpreting something, and is there a better way of doing this? I don't want to have to (pre)declare all of the attributes that I'll be using.


Update:

Now I understand a little better.

If I call $_->Bar::foo, that does not ever get translated into $_->Foo::foo, regardless of @ISA.

Now that I understand that only ->foo has magic, and not ->Bar::foo, it makes fixing the problem easy.

Update^2:

Silly me.  UNIVERSAL::can returns exactly what I wanted: a "magical" code ref that follows inheritance when you call it, so I changed my code to use it.

package Autoattr; our $DEBUG; sub new($) { $DEBUG and printf STDERR "%s::new(%s)\n", __PACKAGE__, join ', ', map "'$_'", @_; ref $_[0] ? bless {%{$_[0]}}, ref $_[0] : bless {}, $_[0]; } sub DESTROY { $DEBUG and printf STDERR "%s::DESTROY(%s)\n", __PACKAGE__, join ', ', map "'$_'", @_; return; } sub AUTOLOAD { no strict qw(refs); $DEBUG and printf STDERR "%s::AUTOLOAD=%s(%s)\n", __PACKAGE__, our $AUTOLOAD, join ', ', map "'$_'", @_; *{$AUTOLOAD} = # into the symbo +l table $AUTOLOAD =~ /(?<![^:])get_([^:]*)$/s ? do { # define a gette +r my $attr = $1; sub($) {ref $_[0] ? $_[0]->{$attr} : ${"$_[0]::$attr"}}; } : $AUTOLOAD =~ /(?<![^:])set_([^:]*)$/s ? do { # define a sette +r my $attr = $1; sub($$) { ref $_[0] ? $_[0]->{$attr} : ${"$_[0]::$attr"} = $_[1] +; $_[0]; }; } : do { # get/set depend +ing on @_ (my $get = $AUTOLOAD) =~ s/([^:]*)$/get_$1/; (my $set = $AUTOLOAD) =~ s/([^:]*)$/set_$1/; sub($;$) {$#_ ? $_[0]->$set(@_[1 .. $#_]) : $_[0]->$get}; } ; $AUTOLOAD =~ /([^:]*)$/; goto $_[0]->can($1); # go to the new +symbol }

Thanks!

Replies are listed 'Best First'.
Re: AUTOLOAD not following @ISA as expected
by diotalevi (Canon) on Aug 02, 2005 at 22:11 UTC
    You'll want to forget you ever read anything about perl's prototyping syntax. It isn't meant to be used and it will introduce bugs that only exist when you use that system. The only safe prototype is (&) and that's rather specialized. Until you can fully explain why prototypes are broken as implemented, you shouldn't use them.

    I can and I don't use them. That's what Params::Validate is for.

      I've been getting into a habit of writing valid prototypes for my functions, for documentation more than anything. They're pointless here, so I took them out...

      Thanks for the pointer, though, I'll look into using Params::Validate in the future.

        That's just the thing, it adds bugs that wouldn't otherwise exist. If you need documentation, do it some other way.
Re: AUTOLOAD not following @ISA as expected
by dave_the_m (Prior) on Aug 02, 2005 at 21:11 UTC
    I haven't got the time or patience to read and try to understand all your code, but I get the impression that you're expecting goto &sub to do inheritance, which it doesn't. Is that what you mean by 'bouncing back of AUTOLOAD'?

    Dave.

      Yes, that is what I mean.
        Yes, that is what I mean
        In that case I think you want to replace sub {goto \&{$#_ ? $set : $get}};
        with
        sub { my $self = shift; @_ ? $self->$set(@_) : $self->$get(@_) };
        or
        sub { goto &{$_[0]->can($#_ ? $set : $get)} };

        Dave.

        AUTOLOAD doesn't play nicely with inheritance -- it takes control of everything. (including DESTROY, which can make for some interesting problems).

        You may want to take a look at NEXT

Re: AUTOLOAD not following @ISA as expected
by borisz (Canon) on Aug 02, 2005 at 21:50 UTC
    I'm to lazy to follow the details, but for Bar,  *{$AUTOLOAD} = sub($;$) {goto \&{ $#_ ? $set : $get}}; can't work, since Bar::set_bar does not exists in opposide to Foo, where Foo::set_bar does the work. Same for Foo::get_foo.
    Boris

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://480295]
Approved by blokhead
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2018-07-18 18:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?















    Results (393 votes). Check out past polls.

    Notices?