The Perl You Need To Know - Part 2
by Stas BekmanMay 07, 2002
Introduction
In this article, we continue to talk about the essential Perl basics that you should know before starting to program for mod_perl.
Tracing Warnings Reports
Sometimes it's hard to understand what a warning is complaining about. You see the source code, but you cannot understand why some specific snippet produces that warning. The mystery often results from the fact that the code can be called from different places if it's located inside a subroutine.
Here is an example:
warnings.pl
-----------
#!/usr/bin/perl -w
use strict;
correct();
incorrect();
sub correct{
print_value("Perl");
}
sub incorrect{
print_value();
}
sub print_value{
my $var = shift;
print "My value is $var\n";
}
In the code above, print_value()
prints the passed value. Subroutine
correct()
passes the value to print, but in subroutine incorrect()
we
forgot to pass it. When we run the script:
% ./warnings.pl
we get the warning:
Use of uninitialized value at ./warnings.pl line 16.
Perl complains about an undefined variable $var
at the line that
attempts to print its value:
print "My value is $var\n";
But how do we know why it is undefined? The reason here obviously is that the calling function didn't pass the argument. But how do we know who was the caller? In our example, there are two possible callers, in the general case there can be many of them, perhaps located in other files.
We can use the caller()
function, which tells who has called us, but
even that might not be enough: It's possible to have a longer sequence
of called subroutines, and not just two. For example, here it is sub
third()
which is at fault, and putting sub caller()
in sub second()
would not help:
sub third{
second();
}
sub second{
my $var = shift;
first($var);
}
sub first{
my $var = shift;
print "Var = $var\n"
}
The solution is quite simple. What we need is a full calls stack trace to the call that triggered the warning.
The Carp
module comes to our aid with its cluck()
function. Let's
modify the script by adding a couple of lines. The rest of the script
is unchanged.
warnings2.pl
-----------
#!/usr/bin/perl -w
use strict;
use Carp ();
local $SIG{__WARN__} = \&Carp::cluck;
correct();
incorrect();
sub correct{
print_value("Perl");
}
sub incorrect{
print_value();
}
sub print_value{
my $var = shift;
print "My value is $var\n";
}
Now when we execute it, we see:
Use of uninitialized value at ./warnings2.pl line 19.
main::print_value() called at ./warnings2.pl line 14
main::incorrect() called at ./warnings2.pl line 7
Take a moment to understand the calls stack trace. The deepest calls
are printed first. So the second line tells us that the warning was
triggered in print_value(); the third, that print_value()
was
called by subroutine, incorrect().
script => incorrect() => print_value()
![]() ![]() From the Frontiers of Research to the Heart of the Enterprise mod_perl 2.0, the Next Generation Stas Bekman will provide an overview of what's new in mod_perl 2.0 and what else is planned for the future in his talk at the upcoming O'Reilly Open Source Convention, this July 22-26, in San Diego. ![]() |
We go into incorrect()
and indeed see that we forgot to pass the
variable. Of course, when you write a subroutine such as print_value
, it
would be a good idea to check the passed arguments before starting
execution. We omitted that step to contrive an easily debugged example.
Sure, you say, I could find that problem by simple inspection of the code!
Well, you're right. But I promise you that your task would be quite
complicated and time consuming if your code has some thousands of
lines. In addition, under mod_perl, certain uses of the eval
operator and ``here documents'' are known to throw off Perl's line
numbering, so the messages reporting warnings and errors can have
incorrect line numbers. This can be easily fixed by helping compiler
with #line
directive. If you put the following at the beginning of
the line in your script:
#line 125
then it will tell the compiler that the next line is number 125 for reporting needs. Of course, the rest of the lines would be adapted as well.
Getting the trace helps a lot.
my()
Scoped Variable in Nested Subroutines
Before we proceed let's make the assumption that we want to develop
the code under the strict
pragma. We will use lexically scoped
variables (with help of the my()
operator) whenever it's possible.
The Poison
Let's look at this code:
nested.pl
-----------
#!/usr/bin/perl
use strict;
sub print_power_of_2 {
my $x = shift;
sub power_of_2 {
return $x ** 2;
}
my $result = power_of_2();
print "$x^2 = $result\n";
}
print_power_of_2(5);
print_power_of_2(6);
Don't let the weird subroutine names fool you, the print_power_of_2()
subroutine should print the square of the number passed to it. Let's
run the code and see whether it works:
% ./nested.pl
5^2 = 25
6^2 = 25
Ouch, something is wrong. May be there is a bug in Perl and it doesn't work correctly with the number 6? Let's try again using 5 and 7:
print_power_of_2(5);
print_power_of_2(7);
And run it:
% ./nested.pl
5^2 = 25
7^2 = 25
Wow, does it works only for 5? How about using 3 and 5:
print_power_of_2(3);
print_power_of_2(5);
and the result is:
% ./nested.pl
3^2 = 9
5^2 = 9
Now we start to understand -- only the first call to the
print_power_of_2()
function works correctly. This makes us think that
our code has some kind of memory for the results of the first
execution, or it ignores the arguments in subsequent executions.
The Diagnosis
Let's follow the guidelines and use the -w
flag:
#!/usr/bin/perl -w
Under Perl version 5.6.0+ we use the warnings
pragma:
#!/usr/bin/perl
use warnings;
Now execute the code:
% ./nested.pl
Variable "$x" will not stay shared at ./nested.pl line 9.
5^2 = 25
6^2 = 25
We have never seen such a warning message before and we don't quite
understand what it means. The diagnostics
pragma will certainly
help us. Let's prepend this pragma before the strict
pragma in our
code:
#!/usr/bin/perl -w
use diagnostics;
use strict;
And execute it:
% ./nested.pl
Variable "$x" will not stay shared at ./nested.pl line 10 (#1)
(W) An inner (nested) named subroutine is referencing a lexical variable defined in an outer subroutine.
When the inner subroutine is called, it will probably see the value of the outer subroutine's variable as it was before and during the *first* call to the outer subroutine; in this case, after the first call to the outer subroutine is complete, the inner and outer subroutines will no longer share a common value for the variable. In other words, the variable will no longer be shared.
Furthermore, if the outer subroutine is anonymous and references a lexical variable outside itself, then the outer and inner subroutines will never share the given variable.
This problem can usually be solved by making the inner subroutine anonymous, using the sub {} syntax. When inner anonymous subs that reference variables in outer subroutines are called or referenced, they are automatically rebound to the current values of such variables.
5^2 = 25
6^2 = 25
Well, now everything is clear. We have the inner subroutine
power_of_2()
and the outer subroutine print_power_of_2()
in our
code.
When the inner power_of_2()
subroutine is called for the first time,
it sees the value of the outer print_power_of_2()
subroutine's $x
variable. On subsequent calls the inner subroutine's $x
variable
won't be updated, no matter what new values are given to $x
in the
outer subroutine. There are two copies of the $x
variable, no
longer a single one shared by the two routines.
