in reply to say statements within 'sub AUTOLOAD'

You need to use strictures, which enable messages telling you what's wrong: use strict; use warnings;. You need to import say, eg with use feature 'say';. Also, you didn't assign the coderef to $method, so there's nothing to go to with goto. And since you're (wisely) not doing string eval, you don't need the eval block (but if you do need it for compatibility with other code, check that $method has been assigned a coderef before trying to execute it). And you need to use Carp; if you're going to put it in your code (but the way it's used, die() would be adequate).

See the following:

#! /usr/bin/env perl package Foo; use strict; use warnings; use feature 'say'; sub AUTOLOAD { (my $method = our $AUTOLOAD) =~ s/.*:://; return if $method eq 'DESTROY'; say "Foo::$method was called"; die "bad method $method\n" if $method ~~ [qw/troz narf/]; if ($method eq 'day') { return +(localtime)[3]; } elsif ($method eq 'month') { return +(localtime)[4] + 1; } } package main; use strict; use warnings; use feature 'say'; say( Foo->day ); say( Foo->troz ); __END__ Foo::day was called 25 Foo::troz was called bad method troz

Replies are listed 'Best First'.
Re^2: say statements within 'sub AUTOLOAD'
by ianyappy (Initiate) on Dec 25, 2010 at 17:22 UTC

    Hi, and many thanks (with a belated Merry Xmas) for the reply. Wish to address it in point form:

    1. Using strictures: I did use those strictures (written outside the braces for the package MyDate). My bad for not reproducing it.. So I don't think its about any error being generated because i didn't declare those strictures. Using print rather than say didn't make any difference either.

    2. Coderefs: I'm not quite sure what you mean by me not assigning a coderef? I defined the subroutines within my if statements.. so if $method was 'day' wouldn't &$method be calling &day? I did get the desired outputs from the subroutines.. so it seems to me like that wasn't a problem either.

    3. Defining subs: I was learning AUTOLOAD (from Intermediate Perl) in the context of dynamically defining new subroutines, while your code only returns the desired value. I presume that means that if I write

    say( Foo->day ); say( Foo->month ); say( Foo->day );

    I would expect the output

    Foo::day was called 26 Foo::month was called 12 26

    rather than the actual output

    Foo::day was called 26 Foo::month was called 12 Foo::day was called 26

    Actually I get the impression that AUTOLOAD isn't even being called. Changing the words in my grep statement or writing if ( $method eq "dayz") {...} doesn't produce any errors while changing the name of my subroutine does. So erm...:S why?

      ianyappy:

      One thing you have to understand about the statement  sub name { ... } is that it always executes at compile time if it is not in a string eval (i.e., is in a block  eval or is a nested subroutine definition in another subroutine). So your impression that "AUTOLOAD isn't even being called" is correct. See example below.

      >perl -wMstrict -le "{ package Foo; use warnings; use strict; our $AUTOLOAD; sub AUTOLOAD { eval { sub foo { return 'FOO'; } }; sub bar { return 'BAR'; } print qq{AUTOLOADing '$AUTOLOAD'}; no strict 'refs'; *$AUTOLOAD = sub { return uc $AUTOLOAD }; goto &$AUTOLOAD; } } ;; print 'foo? ', exists &Foo::foo; print Foo->foo; print 'bar? ', exists &Foo::bar; print Foo->bar; print 'baz? ', exists &Foo::baz; print Foo->baz; print 'baz? ', exists &Foo::baz; print Foo->baz; " foo? 1 FOO bar? 1 BAR baz? AUTOLOADing 'Foo::baz' FOO::BAZ baz? 1 FOO::BAZ

      In this example,  Foo::foo and  Foo::bar already exist at runtime, so  AUTOLOAD is never called for them. The first time  Foo::baz is called,  AUTOLOAD is called and defines the subroutine and also executes the just-defined subroutine. When  Foo::baz is called subsequently, it exists.

      Update: To pound the point about nested subroutine definitions even further into the ground, remove the  sub bar { return 'BAR'; } statement from the  AUTOLOAD function in the example above and replace the  eval statement with
          eval { sub foo { sub bar { return 'BAR'; }  return 'FOO'; } };
      or the even more pathological
          eval { sub foo { return 'FOO';  sub bar { return 'BAR'; } } };
      The results of execution are the same.

        Ooops, what I Really meant was i did (use strict;use warnings;use 5.010;use Carp;) :) Not used to the lingo.

        Now I understand what you mean by eval strings, I didn't realize the significance (eval q{} vs eval{}) but I do now.

        I tried calling the method dynamically without using references. It seems to work just as fine:

        #!/usr/bin/perl use strict; use 5.010; use warnings; use Carp; { package Foo; our $AUTOLOAD; sub AUTOLOAD { eval { sub foo { sub bar { return 'BAR'; } return 'FOO'; } }; + print qq{AUTOLOADing '$AUTOLOAD'}; my $method = $AUTOLOAD; $method =~ s/.*:://; $method eq 'baz' ? eval q{sub baz{ return uc $AUTOLOAD }} :die + "Invalid method."; goto &$method; } } print 'foo? ', exists &Foo::foo; say Foo->foo; print 'bar? ', exists &Foo::bar; say Foo->bar; print 'baz? ', exists &Foo::baz; say Foo->baz; print 'baz? ', exists &Foo::baz; say Foo->baz; ######OUTPUT##### foo? 1FOO bar? 1BAR baz? AUTOLOADing 'Foo::baz'FOO::BAZ #AUTOLOADING prints once baz? 1FOO::BAZ
      1: When I only add strictures, as in the following code: I get the following:
      String found where operator expected at 879141.pl line 6, near "say "G +oing into AUTOLOAD"" (Do you need to predeclare say?) String found where operator expected at 879141.pl line 9, near "Carp:: +croak "Invalid method: $method."" (Do you need to predeclare Carp::croak?) syntax error at 879141.pl line 6, near "say "Going into AUTOLOAD"" syntax error at 879141.pl line 9, near "Carp::croak "Invalid method: $ +method."" BEGIN not safe after errors--compilation aborted at 879141.pl line 26.

      The first error tells me that say needs to be imported, the second that Carp isn't loaded. If nothing was printed for you, then that package didn't have warnings enabled.

      2: Your code looked like this: if (foo) { eval { sub bar {} } }. You were defining a sub inside the AUTOLOAD sub, and then executing that code by name, instead of assigning to another variable a coderef, like $code = sub { ... }; and then later calling it or using goto on it.

      3: Notice that the "was called" statement is executed unconditionally, after it's determined that DESTROY wasn't called.

      Actually I get the impression that AUTOLOAD isn't even being called.
      It looks like you're trying to juggle the differences between the code I provided and the code your provided, and then trying to map them to the actual code you're running. This isn't working.