Having been a long-time gamer, and wanting to stretch my brain around IMPORTING AUTOLOADED FUNCTIONS, I came up with this little package.

package Games::Dice; use warnings; use strict; use vars '$AUTOLOAD'; sub AUTOLOAD { if ( $AUTOLOAD =~ /^.*::(\d+)[Dd](\d+)/ ) { buildsub($AUTOLOAD,$1,$2); goto &$AUTOLOAD; } } sub buildsub { my ($name,$rolls,$die) = @_; my $sub = <<END_SUB; sub $name { my \$result = $rolls; for( my \$i = 0; \$i < $rolls; \$i++ ) { \$result += int(rand($die)); } return \$result; } END_SUB eval $sub; die "Oops, dropped a die" if ($@); } sub import { no strict 'refs'; my $pkg = shift; my $caller = caller(0); for my $dice (@_) { if ($dice =~ /(\d+)[Dd](\d+)/) { buildsub("$pkg\::$dice",$1,$2); *{"$caller\::$dice"} = \&{"$pkg\::$dice"}; } } } 1; # Example use package main; use Games::Dice ( '3d6' ); my %rolls; for (1..1000) { my $roll = main::3d6; # The main:: is required because??? $rolls{$roll}++; } for (sort {$a<=>$b} keys %rolls) { print "$_ => $rolls{$_}\n"; }

Replies are listed 'Best First'.
Re: Perlish dice for gamers (oh, and importing autoloaded functions)
by blakem (Monsignor) on Sep 08, 2002 at 06:08 UTC
    my $roll = main::3d6; # The main:: is required because???
    Probably because subs that start with digits are frowned upon. Try tweaking it so your subs start with a char instead... Perhaps r3d3() in this case.

    -Blake

Re: Perlish dice for gamers (oh, and importing autoloaded functions)
by TStanley (Canon) on Sep 08, 2002 at 13:59 UTC
    A quick search on CPAN turned up these modules

    TStanley
    --------
    It is God's job to forgive Osama Bin Laden. It is our job to arrange the meeting -- General Norman Schwartzkopf
      I think you're missing the point... Solo was trying to figure out a way to impliment dynamicly created functions which could be both AUTOLOADed and imported.

        I should have reversed my title to 'Importing autoloaded functions (oh, and Perlish dice for gamers)'. Sorry if that was a misleading attempt at wit.
Re: Perlish dice for gamers (oh, and importing autoloaded functions)
by hossman (Prior) on Sep 09, 2002 at 05:55 UTC
    I've been thinking alot about your post the past few days. It strikes me as a really cool idea, but some things about your solution bother me...

    ...mainly, I feel like you aren't really AUTOLOADing anything. Your import method is using eval to dynamicly create the method -- at that point it exists, and the AUTOLOAD isn't needed. (unless you add some direct calls to Games::Dice->foo, but even then: the eval called by your AUTOLOAD is dynamicly creating the method, after the first invokation, your AUTOLOAD won't be used.

    I tweaked your code some (see below), mainly to replace the eval in the AUTOLOAD with a call out to a new "roll" method, and the eval in your import with some direct symbol table munging.

    The problem is, This still isn't "importind autoloaded functions" ... anything a client tries to import, is acctually created at import time.

    Can anybody out there think of a way to genuinely create an entry the symbol table for a package that points to "AUTOLOAD" ?

      I think calling mainGames::Dice::r3d3 or somesuch the first time without importing it first will trigger the AUTOLOAD entry point.
        you can't call main::r3d3 without importing first, if you call Games::Dice::r3d3, then yes, the AUTOLOAD will be used but only the first time, after that the method will allready exist.
      Inheriting Exporter is good! :)

      hossman wrote:

      Can anybody out there think of a way to genuinely create an entry the symbol table for a package that points to "AUTOLOAD" ?

      I was in the middle of typing why this couldn't be done, when I talked myself into a way of trying it! What do you think of this?

      sub import { no strict 'refs'; my $pkg = shift; for my $dice (@_) { if ($dice =~ /r(\d+)[Dd](\d+)/) { # my $meth = \sub { roll($1, $2); }; my $meth = \sub {"__PACKAGE__::$dice"}; *{$pkg . '::' . $dice} = $meth; } } @EXPORT_OK = @_; Smonk->export_to_level(1, $pkg, @_); }
        my $meth = \sub {"__PACKAGE__::$dice"};
        *{$pkg . '::' . $dice} = $meth;
        Ya know, i stared at that for about 20 minutes trying to figure out why it worked. It seemed to me that all the first line should do is create an anonymous sub that returns "__PACKAGE__::$dice" ... so i did some digging, and sure enough that *IS* all it does.

        I don't know what you and I have been doing wrong with our attempts at putting things in the package's symbol table, but evidently none of it was doing anything (at least: not anything that affected what we were testing).

        It turns out that THIS is all you need to import an AUTOLOADed method...

        sub import { my $pkg = shift; @EXPORT_OK = @_; __PACKAGE__->export_to_level(1, $pkg, @_); }

        But for the sake of correctness, it should probably only copy things into @EXPORT_OK that match the regexp -- that way writting "use Smonk qw(r3d3 foo)" will generate...

        "foo" is not exported by the Smonk module at monk.pl line 4 Can't continue after import errors at monk.pl line 4 BEGIN failed--compilation aborted at monk.pl line 4.