Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Using import to generate subroutines

by Thilosophy (Curate)
on Nov 24, 2004 at 01:27 UTC ( [id://410032]=perlquestion: print w/replies, xml ) Need Help??

Thilosophy has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

I have a question about the import/export mechanism.

I would like to write an "import" that dynamically creates subroutines and installs them into the calling module. The content of the subroutine can be derived from just the subroutine name.

For example:

package CallingModule; use MyProcedures qw( Wow ); Wow();

The subroutine Wow, for example

sub CallingModule::Wow{ print "Wow"; }
would have been created by MyProcedures::import .

I figure I cannot use the standard Exporter for this, so how do I write my own "import" ? Just eval() a string that produces the subroutine? Or make a closure and assign it to the caller's symbol table? Or somehow else?

Replies are listed 'Best First'.
Re: Using import to generate subroutines
by revdiablo (Prior) on Nov 24, 2004 at 01:42 UTC
    how do I write my own "import" ?

    This is one of the few cases where I think symbolic refs make things much easier. Here's how I'd probably do it:

    package MyProcedures; use strict; use warnings; sub import { my $class = shift; my $caller = (caller)[0]; no strict 'refs'; for my $f (@_) { *{"$caller\::$f"} = sub { print "$f\n" }; } } 1;

    And then it can be used like so:

    $ perl -e 'use MyProcedures qw(one two three); one(); two(); three();' one two three
      no strict 'refs'; for my $f (@_) { *{"$caller\::$f"} = sub { print "$f\n" }; }
      Thanks, that works.
        Although it makes no difference here, in general it's better to write that as
        for my $f (@_) { no strict 'refs'; *{"$caller\::$f"} = sub { print "$f\n" }; }
        This limits the scope of the no strict 'refs'. Otherwise, if you add any more code after the foreach, it will not be checked with strict.
Re: Using import to generate subroutines
by tachyon (Chancellor) on Nov 24, 2004 at 01:59 UTC

    Are you sure you just don't want to install an AUTOLOAD routine?

    package Foo; bar(1,2,3); sub AUTOLOAD { my @args = @_; my ( $package, $func ) = split "::", $AUTOLOAD; print "ARGS: @args\nPKG: $package\nFUNC: $func\n"; }

    cheers

    tachyon

      > Are you sure you just don't want to install an AUTOLOAD routine?

      I was thinking about that. I ran into two problems, however.

      1) I still want to have things set themselves up with "use", so that the caller of my module does not have to mess with my modules internal functions (which he would have if he wanted to write an AUTOLOAD). So I was thinking to export an AUTOLOAD into the caller's namespace, but maybe that is evil.

      2) What I am really trying to do is make Oracle PL/SQL procedures appear like Perl procedures. PL/SQL procedures can have characters in them that are not allowed for Perl subroutines, most importantly a dot. If my Oracle function is called dbms_random.random, I can map that to a Perl function dbms_random_random, but that is a one-way mapping: An AUTOLOAD for dbms_random_random has no (easy) way to figure out if it is supposed to call dbms_random.random or dbms.random_random.

      Anyway, looking at AUTOLOAD was really worthwhile, makes one appreciate Perl even more.

        An AUTOLOAD for dbms_random_random has no (easy) way to figure out if it is supposed to call dbms_random.random or dbms.random_random.

        Well actually you have the same problem if you are exporting functions with the added disadvantage of tons of redundant code.

        sub AUTOLOAD { my ( undef, $funcname ) = split '::', $AUTOLOAD; my $mapping = { foo => 'foo.bar', bar => 'foobar', }; if ( exists $mapping->{$funcname} ) { # exec $mapping->{$funcname} } else { die "Can't do $funcname, NFI how to!\n"; } }

        cheers

        tachyon

        PL/SQL procedures can have characters in them that are not allowed for Perl subroutines, most importantly a dot.
        Actually, this is wrong =]. To demonstrate:
        *{"dbms_random.random"} = sub { print "I am dbms_random.random\n" } "dbms_random.random"->();
        I have no idea what practical use this has, but I thought you should know!

        You might want to consider just exporting a hash of functions. That way all the functions are in one place for easy listing and so forth and you don't have to worry about mapping subroutine names, as hash keys can contain any character (which is why the subroutine definition code above works..)
Re: Using import to generate subroutines
by rrwo (Friar) on Nov 24, 2004 at 11:23 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-04-24 00:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found