How do I get the following code to work?
use strict;
use warnings;
if ($^O eq 'MSWin32' || $^O eq 'MSWin64') {
use Win32;
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
}
else {
print "Do not know how to do msgbox under UNIX!\n";
}
The above runs under Windows. But under UNIX, there is a compilation error as Win32 cannot be found. Replacing "use" with "require" makes things worse -- the code would fail to compile under both Windows and UNIX because the line containing MB_ICONINFORMATION is always compiled and "MB_ICONINFORMATION" would be an undeclared bare-word.
So how do I get around this problem?
Perl compiles code first to an intermediate representation, then executes it. Since the if
is evaluated at runtime but the use
is handled during compilation, you are not importing the module conditionally.
To fix this, there are a number of possible strategies:
use if
pragmarequire
the moduleeval
To import a module only when a certain condition is met, you can use the if
pragma:
use if $^O eq 'MSWin32', 'Win32';
You can also run code during compilation by putting it into a BEGIN block:
BEGIN {
if ($^O eq 'MSWin32') {
require Win32;
Win32->import; # probably not necessary
}
}
That BEGIN block behaves exactly the same like the above use if
.
Note that we have to use require
here. With a use Win32
, the module would have been loaded during the compile time of the begin block, which bypasses the if
. With require
the module is loaded during runtime of the begin block, which is during compile time of the surrounding code.
In both these cases, the Win32
module will only be imported under Windows. That leaves the MB_ICONINFORMATION
constant undefined on non-Windows systems. In this kind of code, it is better to not import any symbols. Instead, use the fully qualified name for everything and use parentheses for a function call (here: Win32::MB_ICONINFORMATION()
). With that change, just using a require
instead of an use if
may also work.
If you need code to be run later, you can use a string-eval. However, this potentially leads to security issues, is more difficult to debug, and is often slower. For example, you could do:
if ($^O eq 'MSWin32') {
eval q{
use Win32;
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
1;
} or die $@; # forward any errors
}
eval
silences any errors by default, you must check success and possibly rethrow the exception. The 1
statement makes sure that the eval'ed code returns a true value if successful. eval
returns undef
if an error occurs. The $@
variable holds the last error.q{...}
is alternative quoting construct. Aside from the curly braces as string delimiters it is exactly the same as '...'
(single quotes).If you have a lot of code that only works on a certain platform, using the above strategies for each snippet is tedious. Instead, create a module for each platform. E.g.:
Local/MyWindowsStuff.pm:
package Local::MyWindowsStuff;
use strict;
use warnings;
use Win32;
sub show_message {
my ($class, $title, $contents) = @_;
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
}
1;
Local/MyPosixStuff.pm:
package Local::MyPosixStuff;
use strict;
use warnings;
sub show_message {
warn "messagebox only supported on Windows";
}
1;
Here I've written them to be usable as classes. We can then conditionally load one of these classes:
sub load_stuff {
if ($^O eq 'MSWin32') {
require Local::MyWindowsStuff;
return 'Local::MyWindowsStuff';
}
require Local::MyPosixStuff;
return 'Local::MyPosixStuff';
}
my $stuff = load_stuff();
Finally, instead of putting a conditional into your code, we invoke the method on the loaded class:
$stuff->show_message('Aloha!', 'Win32 Msgox');
If you don't want to create extra packages, one strategy is to eval a code ref:
sub _eval_or_throw { my ($code) = @_; return eval "$code; 1" or die $@ }
my $show_message =
($^O eq 'MSWin32') ? _eval_or_throw q{
use Win32;
sub {
Win32::MsgBox("Aloha!", MB_ICONINFORMATION, 'Win32 Msgbox');
}
} : _eval_or_throw q{
sub {
warn "messagebox only supported on Windows";
}
};
Then: $show_message->()
to invoke this code. This avoids repeatedly compiling the same code with eval
. Of course that only matters when this code is run more than once per script, e.g. inside a loop or in a subroutine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With