Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Passing parameters to a module's import function

by rrwo (Friar)
on Feb 12, 2001 at 04:19 UTC ( [id://57813]=perlquestion: print w/replies, xml ) Need Help??

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

I'd like to pass parameters to a module during importation, such as:

  use MyModule { foo => 1, bar => 'two' };

No such facility exists in Exporter, so before I write my own import function, is there a module or code which already does this?

I need to pass options to the module that are accessible from the BEGIN block. It makes coding much easier than to have settings subroutines which destroy objects created by BEGIN and then recreate them. But that seems my only other alternative.

(On my list of things to do I may look into adding this as a patch to Exporter... it would be really useful.)

Replies are listed 'Best First'.
Re (tilly) 1: Passing parameters to a module's import function
by tilly (Archbishop) on Feb 12, 2001 at 04:34 UTC
    Careful before saying that no such facility exists in Exporter. There are, in fact, at least 3 ways to do this while using Exporter.
    1. Use the export_to_level method.
    2. Do like Carp does and have flags listed in @EXPORT_FAIL and processed in an export_fail function.
    3. Write a custom import that looks like this:
      sub import { my $self = shift; my @out; foreach (@_) { if (ref($_)) { # Do one thing } else { push @out, $_; } } @_ = ($self, @out); goto &Exporter::import; }

    UPDATE
    Two quick notes. First of all I had put the case of Exporter wrong. Oops. Secondly the point here is that you can preprocess the list of arguments being imported in any way you please before Exporter sees them. (eg Take references out of the list and do something useful with them.)

      Um, tilly, in the sample usage:

      use MyModule { foo => 1, bar => 'two' };

      are you suggesting that having Exporter.pm know about not just "foo" and "bar" but also "1" and "two" and all other possible parameter values and then having it either export them or report them as failing one at a time is somehow useful? (And your third option seems to have little to do with what was being asked.) (:

      No, Exporter.pm does not provide any way of dealing with such options. You can look at Win32::TieRegistry for an example of how to parse such options while still supporting standard exporting (and non-standard exporting). But you'll probably want to ignore most of the code in Win32::TieRegistry::import as I doubt you'll want to support the strange exporting that is supported there. You might have more luck looking at Win32::TieRegistry::SetOptions.

      Update: I hate this font. () and {} are nearly identical. The stuff I was talking about deals with this:

      use MyModule ( foo => 1, bar => 'two' );

      kind of stuff. ):

              - tye (but my friends call me "Tye")
        I gave 3 methods.

        One of which I specifically said is only useful for flags.

        The other two, including my actual code sample (which was very specifically designed to handle an anonymous hash as a case) work just fine.

      I've already been experimenting with something like the import function you've shown. But by "no such facility exists in Exporter" I mean that you can't have the following in your module:

      package MyModule; use Exporter; @ISA = qw( Exporter ); @EXPORT_OK = qw( mysub anothersub ); @ATTRIBUTES = qw( foo bar );

      so that in your module you could use:

      use MyModule qw( anothersub ), { foo => 1 };

      It would be a good feature for Exporter. (Ideally one would specify required attributes and maybe specify types, default values, etc.).

      UPDATE: Ok, I've corrected the formatting. Didn't realize CODE was converted to PRE.

        First of all please use <code> tags rather than doing your own formatting. (Cleaner, people can download your code, etc.)

        Anyways this feature makes me deeply suspicious. What is this for? Personally I like using modules that are designed to be used multiple times in multiple places. This looks like it would be used to initialize a number of globals in the module. That would mean that if I used modules A and B which both used C, I could get hosed. But I shouldn't need to know anything about what A and B do internally. Therefore C should be designed so that can be used by multiple modules at the same time without any conflict.

        Hmmmm..

        I could be very wrong, but my inclination would be to ask how your module is structured, and ask whether there is a cleaner design that could be used. For instance use an OO style where you pass parameters into new()...

        Incidentally your comment about specifying types raises another design flag for me. Perl is untyped, and does not have a type system in the sense that you see in other languages. Trying to mix concepts from a type system with Perl is generally a sign that you are doing something wrong (or using the wrong tool).

        I don't mean to sound so down on your feature request. Why don't you describe in more detail what a sample problem is where you would find this useful, and we can see whether there is another way to organize this?

      An aside. Here's another way of coding it:

      sub import { my $self = shift; my @exports = grep { "HASH" ne ref($_) } @_; my @options = grep { "HASH" eq ref($_) } @_; foreach (@options) { # handle options here } @_ = ($self, @exports); goto &Exporter::import; }

      Question: will grep interfere with @_ (making the above a bit dangerous)?

        grep() won't do anything bad to @_, but I am personally against doing two operations in a row like you did that are complements.

        That being said, I'd rewrite the @_-filtering line as:
        my %args; push @{ $args{ref($_) eq 'HASH' ? 'opt' : 'ext'} }, $_ for @_;
        Or, I might even do a moving-splice() trick:
        sub import { my $self = shift; my ($i,$j,@options); for (@_) { if (++$j, ref($_) eq 'HASH') { push @options, splice(@_, $j + --$i, 1); $_ = $_[$j+$i], redo if $j + $i < @_; } } # handle @options # @_ now only holds elements which aren't hash refs $self->SUPER::import(@_); }
        </code>

        japhy -- Perl and Regex Hacker
Re: Passing parameters to a module's import function
by repson (Chaplain) on Feb 12, 2001 at 17:08 UTC
    In addition to the methods above to achive what you asked, you may like to explore other methods for whatever you are doing.

    If you are setting data within MyModule as constants during running then other methods may be better.

    # simple setting use MyModule qw/function/; $MyModule::foo = 1; $MyModule::bar = 'two'; function(); # or simple function args use MyModule qw/function/; function( foo => 1, bar => 'two' ); # or OO use MyModule; my $instance = MyModule->new( foo => 1, bar => 'two' ); $instance->function;
    If however you want the data in the anonymous hash to be passed to a function which decides on exporting methods (which is what Exporter and the import method are usually for) the you may want to rethink if exporting in that way is what you want. Then if you still want to do it step back and think again. And if you still have a good reason to do it that way that go ahead and follow the other advice above.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (6)
As of 2024-03-28 10:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found