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

RE: RE: RE: RE: Checking to see if a particular Module is installed

by merlyn (Sage)
on Aug 11, 2000 at 17:18 UTC ( [id://27510]=note: print w/replies, xml ) Need Help??


in reply to RE: RE: RE: Checking to see if a particular Module is installed
in thread Checking to see if a particular Module is installed

Your code fails to import prototypes early enough (at compile time) to influence the proper parsing of the later code. For example, say a constant was defined in GD as:
package GD; sub BIGFONT () { 35 }
Your code would cause this to be misparsed:
my $result = BIGFONT + 3;
because without the empty prototype, BIGFONT is a normal subroutine, and the +3 will be parsed as an argument to the subroutine. With the prototype, we get the proper behavior (and even compile-time constant folding). So, with compile-time importing, we get 38 for the answer, and without, we get 35.

So, to fix this, wrap my code in a BEGIN block, like so:

my $have_GD = 0; BEGIN { eval { require GD }; unless ($@) { GD->import(); $have_GD = 1; } }
Your code does OK without considering prototypes. You haven't been burned, but I'd add the word "yet" there.

-- Randal L. Schwartz, Perl hacker


update: Get rid of the "= 0" on the intialization. Sorry.

Replies are listed 'Best First'.
Conditional compile-time magic (RE: Checking to see if a particular Module is installed)
by tye (Sage) on Aug 11, 2000 at 20:25 UTC

    [ Aside: Yes, I discussed that briefly in my node. That is why I said that I didn't get your point (in the context of the discussion). Also, I assumed that you must have been getting at something else since your reply included code with exactly the same problem (as did your own code earlier in the thread). My node explicitly mentions not being able to use imported subs via barewords, so using that specific case as an example of where I was wrong doesn't make much sense. :) And you are wrong about me not having been burned. I've had quite a bit of fun battling conditional module use (and conditional constant definition) with compile-time magic. Now back to the technical discussion. ]

    You can't use a module's compile-time magic (like prototypes) when you conditionally use the module, unless you conditionally compile all of the code that uses that magic. Like I said, there are ways to do this, but none of them are good.

    There is only one way that I've actually used that survived very long. You can, at compile time, conditionally compile a sub that makes use of the compile-time magic. For example:

    BEGIN { my $code= '...some work-around for not having GD...'; if( eval { require GD } ) { $code= '...code that uses GD...'; } eval "sub mySub { $code }; 1 " or die "$@"; }

    But doing this in order to use bareword constants is rarely worth it (though it does give you compile-time checking for half of your typos).

    So you are often better to just not use the compile-time magic. For example, consider this pretend module that makes other uses of prototypes:

    package Pretend; use base qw(Exporter); our @EXPORT_OK= qw( sorter hasher ); sub sorter(&@); sub hahser(\%); #[...]

    You might try to work-around a possible lack of this module via:

    my $got_Pretend; BEGIN { if( $got_Pretend= eval { require Pretend } ) { Pretend->import( qw( sorter hasher ) ); } else { require WorsePretendWorkAround; WorsePretendWorkAround->import( qw( comparer worse_hasher ) ); } } #[...] if( $got_Pretend ) { @list= sorter { $_[0] <=> $_[1] } hasher %hash; } else { @list= sort \&comparer worse_hasher(\%hash); }

    And if you never bother to test your code when the Pretend module isn't installed, then you might think you've got a pretty good fix. However, this code will just fail to compile if there is no Pretend module (well, it certainly will if you use strict, but you always do that). If that were an okay failure mode, then you might as well just write use Pretend!

    But you can work-around this possible lack of compile-time magic by just avoiding your use of the magic:

    if( $got_Pretend ) { @list= sorter( sub { $_[0] <=> $_[1] }, &hasher(\%hash) ); } else { @list= sort \&comparer worse_hasher(\%hash); }

    Unfortunately, this solution won't tell you when the interface to hasher changes. But there are enough problems with using current Perl prototypes for this kind of checking that you probably won't ever run into that.

    Another solution that sounds very nice but that I've never actually used, is to put the module-dependant code into a separate file and require that file (at compile time) if the module is present.

    Anyway, the one simple thing that everyone should remember about this problem is:

    • If you conditionally use a module, then you need to test your code both with and without the module!

    (And if you conditionally use N modules, then you need to test your code in the 2^N cases of module availability!)

            - tye (but my friends call me "Tye")
RE (5): Checking to see if a particular Module is installed
by tilly (Archbishop) on Aug 11, 2000 at 18:28 UTC
    Absolutely, but I agree with Tom C. on the (in)advisability of prototypes so I cannot bring myself to care much.

    Besides which, my preference is to write a compatibility wrapper which avoids all problems at the cost (in an interpreted language) of a speed hit. YMMV. (Judging from how compatibility winds up being done time and again, most people's mileage does vary in fact.)

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-04-19 05:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found