dcsimg

The Perl You Need to Know, Part II: Working with Nested Subroutines

By Stas Bekman (Send Email)
Posted Sep 1, 2000


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

In his next installment of Perl programming basics, Stas Bekman looks at nested subroutines and provides several ways to deal with them successfully.

Sometimes it's very 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  = shift;
    print "My value is \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 at the line that attempts to print its value:

  print "My value is \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 us very much:

  sub third{
    second();
  }

  sub second{
    my  = shift;
    first();
  }
  sub first{
    my  = shift;
   print "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 {__WARN__} = \&Carp::cluck;
  
  correct();
  incorrect();
  
  sub correct{
    print_value("Perl");
  }
  
  sub incorrect{
    print_value();
  }
  
  sub print_value{
    my  = shift;
    print "My value is \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()

We go into incorrect() and indeed see that we forgot to pass the variable. Of course when you write a subroutine like 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!

Page 1 of 7


Comment and Contribute

Your name/nickname

Your email

(Maximum characters: 1200). You have characters left.