Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Re: A simple import() for those special moments (bugfixes)

by ihb (Deacon)
on Jan 20, 2003 at 12:20 UTC ( [id://228329]=note: print w/replies, xml ) Need Help??


in reply to A simple import() for those special moments

It will simply export any symbol, regardless of its type (function, hash, array etc) whose name matches the pattern in $exported - in this case all the variables declared with vars and the foo_bar function.

One should perhaps also point out that variables declared with our() will be exported too. I've realized that some people are confused about what our():ed variables really are.

But something really nasty can happen. It might export whole packages! Indeed, it's not very likely, but there's still a risk. Example:

{ package Foo::foo_bar; our $bar = 'BAR'; } use Foo; print $foo_bar::bar; # 'BAR'!

There's also a bug in the code. If the caller is a nested package, e.g. A::B, then it won't work. This is due to $main::{$caller}->{$_}. The keys in %main:: (or %:: for short) that end in :: are just the first part of the package name. This behaviour nests, so A::B's (the ' is not a package delimiter here ;)) symbol table is found in %{$main::{'A::'}{'B::'}}.

This being a copy-n-paste candidate make those hardcoded Foos hurt an eye of mine. I'd really like see __PACKAGE__ utilized here. Each time you copy this you'll (or someone else who rip this code) have to change those package names and that increases the risk of getting a bug.

To the real question; if you should be wary of this. Unless you've made it very general, i.e. works for all callers and packages, I would. Unless you're totally sure there's no bug, I would. There will be quite a few modules to patch after a year or two if you just copy it.

Below is my version, with minimal changes from the original:

sub import { my $caller = caller() . '::'; do { require Carp; Carp::croak("You cannot specify an import list to " . __PA +CKAGE__); } if @_ > 1; no strict 'refs'; *{"$caller$_"} = *{__PACKAGE__ . "::$_"} for grep !/::$/ && /$exported/, keys %{__PACKAGE__ . '::'} +; }

I don't claim this version to be bug free either. :)

Btw, how about making this into a module and calling it Exporter::Pattern? (Or Exporter::Regex(p), but I like Exporter::Pattern better.) The interface would be quite simple:   use Exporter::Pattern qr/PATTERN/;

ihb

Replies are listed 'Best First'.
Re^2: A simple import() for those special moments
by Aristotle (Chancellor) on Jan 20, 2003 at 16:23 UTC

    Thanks for the constructive comments - that's the kind of reply I was hoping for.

    All points accepted. I tried to change it with your arguments in mind and noticed an obvious, major caveat along the way I hadn't picked up on before because I still don't fully grok typeglobs: it exports the entire typeglob - lock, stock and barrel. If an importing package already has a $foo_bar, this routine will overwrite the typeglob in the process of exporting &foo_bar, even if it never exports a $foo_bar of its own.

    Oops.

    That was not what I had in mind. So I pulled out my Camel and read up on typeglobs, the section that talks about the *sym{THING} syntax and tried grepping the list of slots of each glob. That works fine - except the SCALAR slot always contains a reference, whether such a package wide scalar has been named or not. In other words, if *x{SCALAR} is \undef, there is no way to know whether it wasn't mentioned at all (and should therefore not be exported) or it was mentioned in such as undef $x; or use vars qw($x); which define but do not assign a value to the variable.

    Which means this approach is not viable..

    Makeshifts last the longest.

      it exports the entire typeglob

      I thought that was intentional. :)

      If an importing package already has a $foo_bar, this routine will overwrite the typeglob in the process of exporting &foo_bar, even if it never exports a $foo_bar of its own.

      It's more likely to be the other way around, i.e.

      sub foo_var { 'a' } use Foo; foo_var(); # Undefined subroutine called.
      It's easy to check if a glob gets overwritten though.

      This isn't such a big issue as you seem to think. Problems will only occure when you import after defining your own data types. Special exception for subroutines though. They can be declared and that (including the prototype) can disappear. E.g.

      sub foo_var ($$); use Foo; foo_var;
      complains under strict because &foo_var isn't declared anymore when foo_var; is found.

      Anyway, usually modules are use()d before subroutines are defined, and subroutines are usually defined before variables. So it's not that bad. This works perfectly (albeit a bit dangerous):

      use Foo; sub foo_var { 1 } foo_var();

      Cheers,
      ihb
        Good point. Maybe a viable compromise would be to check for the existence of the target glob and croak if one is already there?

        Makeshifts last the longest.

        I knew there was a reason I didn't feel comfortable with it:
        #!/usr/bin/perl BEGIN { $SIG{__WARN__} = sub { print "Warn in package ".caller().": ", + @_ }; package X; $foo = "I'm in X, export me!"; *Y::foo = *foo } package Y; sub foo { "Oh dear.. I wasn't meant to be outside Y" } warn $foo; package X; warn foo(); __END__ Warn in package Y: I'm in X, export me! at t.pl line 11. Warn in package X: Oh dear.. I wasn't meant to be outside Y at t.pl li +ne 14.

        In other words.. all globals exported by copying the entire typeglob will have all the packages that import them sharing all globals with the respective names - and this is more than "just" a heavy caveat.

        Makeshifts last the longest.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (7)
As of 2024-03-28 08:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found