Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to catch a runtime error while calling a non-exist subroutine 2

Status
Not open for further replies.

whn

Programmer
Oct 14, 2007
265
0
0
US
Say I have a sample code like this:

Code:
&funcc(); # Mis-spelling. It should be func, instead of funcc

sub func {
  # some implementation here
}

The above piece of code will compile and will die at runtime cause the mis-spelling w/o any helpful error messages.

Is there a way to catch this kind of runtime error? I hope there is a way to print a line somewhat like "Subroutine funcc called at line ### is undefined...".

Thanks for your help.
 
How much control do you have over the calling sequence?

Here is an example (watered down) of what I've used
Code:
$sub_to_call = 'func' ; # or passed in value
if (! exists &{$sub_to_call}) {
   print "sub $sub_to_call - invalid\n";
   exit 0 ; # (or) next, return, die, etc)
};
&{$sub_to_call}() ;
That I know, the -w flag or 'use strict' doesn't catch the func vs. funcc because subroutines can be defined ad-hoc. So while funcc may not exist at start up, your code can define it as it is running. I have a few programs that generate and execute their own subs so I hope the perl gods never deem this an oversight and change this behavior.
 
Thank you Pinkey!!

What if I had hundreds subroutines? This kind of check would be tiresome.

Somehow, I had an impression that there is a built-in veriable in perl. Once this variable is set or turned on, then an error message including the line number where the subroutine is called would be printed out automatically. I just don't remember what that built-in variable was or I am not sure if I remembered it correctly.

But, again, your help is highly appreciated.
 
whn said:
I hope there is a way to print a line somewhat like "Subroutine funcc called at line ### is undefined..."
I'm not sure what you're running this on (and I don't have any other platforms to test it on at the moment) but runing Activestate 5.10 I get
Code:
>perl -i
&test();
&ttest();

sub test {
  print "got here!\n";
}
^D
got here!
[b][blue]Undefined subroutine &main::ttest called at - line 2.[/blue][/b]

I'm fairly certain I've seen similar error messages from other distros but I may be mistaken. Are you not getting anything like that? Normally, the hyphen in the above error message is replaced with the file name where the problem exists.
 
Thank you, rharsh. The error message you posted is the exact one I am looking for. However, I am not getting anything like that at all. The programm just dies.

I must have missed something very simple.

BTW, I am having this problem on all platforms (solaris, linux, aix, cygwin on windows). The perl version is 5.8 & 5.10 (it is active perl on windows, but I don't remember the version at this moment).

Thanks again.
 
Try running the code I posted, see what it does.

Is it possible you're redirecting STDERR to a log file somewhere? Is this part of a CGI script? Could the errors be ending up in the log files for your web server?
 
Ahh - misread first post.

All platforms?! Any chances STDERR has been modified?

(A couple of hit-n-miss questions)
- Do you get error messages of any kind from perl? (i.e. invalid syntax, zero division, invalid use of xxxx, etc)
*** Looking to quantify if compile errors, run time errors or both are not showing up.
- Both may indicate something at the OS level (i.e. your shell is configured to automatically route STDERR to a log file somewhere)...I believe utilities like crontab do this as well.
- If just run-time, is any of the code (including that brought in by use or require) working with the %SIG var? $SIG{__WARN__} = sub {}; will effectively turn off your warnings.
- Something like this will also effect error messages: open(STDERR, ">>", \$some_var) ;
That will dump your STDERR messages into a variable. Don't recall why a user needed to do this, but had come across another post where that is what they wanted.
 
Thank you both, rharsh & Pinkey, for your help!! After having read your posts, I realize that I did not understand what my problem was and thus I gave some incorrect information. Now I know what my problem is and please allow me to ask my question again. It is a bit lengthy, though.

I have two sample codes here. The names and contents are listed below:

Code:
[b]% ls run.pl test/*.pm[/b]
run.pl*  test/t2.pm*

[b]% cat run.pl[/b]
my $test = $ARGV[0];
require "$test.pm";

my @list = &load_test($test);
&run_test($test, @list);

exit;

sub load_test {
  my ($test) = @_;
  my @results = ();
  my $name = (split(/\//, $test))[1];

  my $self = {};
  bless ($self, $name);
  push @results, $self;
  return @results;
}

sub run_test {
  my ($tests, @testinsts) = @_;
  foreach my $testinst ( @testinsts ) {
    print "\n$0..run_scenarios(). To excute $testinst.\n";
    [b]eval {$testinst->run_scenario()};[/b]
  }
}

[b]% cat t2.pm[/b]
package t2;

sub run_scenario {
  print "in t2.pm..run_scenario(), to call t2.pm..test1()\n";
  &test2(); [b]# mis-spell. Should be test1[/b]
  print "in t2.pm..run_scenario(), after calling t2.pm..test1()\n";
}

sub test1 {
  print "Subroutine t2.pm..test1() is alled.\n";
}

1;

First test run w/o mis-spell:
Code:
[b]% perl run.pl test/t2[/b]

run.pl..run_scenarios(). To excute t2=HASH(0x101e4ec8).
in t2.pm..run_scenario(), to call t2.pm..test1()
Subroutine t2.pm..test1() is alled.
in t2.pm..run_scenario(), after calling t2.pm..test1()

So far, everything is expected.

Second test run with the intentional mis-spell:
Code:
[b]% perl run.pl test/t2[/b]

run.pl..run_scenarios(). To excute t2=HASH(0x101e4ec8).
in t2.pm..run_scenario(), to call t2.pm..test1()

As you can see here, the program dies without any helpful messages. I know this must have something to do with eval() in run.pl. My question is whether there is a way to fix the problem so that we can get some kind of error messages?

BTW, here is some background about my question. The codes listed above were derived from my department’s automation frame work, which involves a few hundreds files. Many people have worked on these files and mis-spells are all over the places. Right now, I am conducting some clean-ups and wish I could find a way to make those codes print the error message out.

I hope I have made myself clear this time and thank you so much for your time and help.

 
perlfunc said:
If there is a syntax error or runtime error, or a die statement is executed, an undefined value is returned by eval, and $@ is set to the error message.
Your code doesn't die, it executes to the end; you should either save the content of [tt]$@[/tt] in an array for each [tt]eval[/tt] execution, and check the content after [tt]run_test()[/tt] has finished, or use the fact that no message is printed to detect errors, or use a syntax like [tt]&$subname();[/tt] instead of [tt]eval[/tt] , as suggested above by PinkeyNBrain, to die on error.

Franco
: Online engineering calculations
: Magnetic brakes for fun rides
: Air bearing pads
 
Thank you, prex1. Your answer helps a lot.
 
I need to follow up this thread a bit more.

First of all, the built-in variable $@ works fine. Thanks to prex1!!

However, I can not fully incorporate Pinkey’s suggestions in my code.

I replaced eval() with Pinkey’s suggestion as shown below:

Code:
sub run_test {
  my ($tests, @testinsts) = @_;
  foreach my $testinst ( @testinsts ) {
    print "\n$0..run_scenarios(). To excute $testinst.\n";
    [b]&{$testinst->run_scenario()}; # [COLOR=red]This is line 24[/color][/b]
  }
}

The rest of the codes are all the same as listed in my previous post.

And here is one test run:

Code:
% perl run.pl test/t2

run.pl..run_scenarios(). To excute t2=HASH(0x101e4ec8).
in t2.pm..run_scenario(), to call t2.pm..test1()
Subroutine t2.pm..test1() is alled.
in t2.pm..run_scenario(), after calling t2.pm..test1()
[b]Undefined subroutine &main::1 called at run.pl [COLOR=red]line 24[/color].[/b]

So, how to get rid of the “Undefined subroutine…” error?

Thanks.
 
Never worked with the 'bless' function so that one is a new area for me. Noting that printing of the bless'ed var gives the equivalent of: t2=HASH(0x101e4ec8) is something I'll have to read more on. Nonetheless tried the following:
Code:
    # The {$testinst->run_scenario()} portion returns a '1' then the
    # parser tries to execute a sub called &1() which doesn't exist.
    &{$testinst->run_scenario()};

    # Similar to above -- parser can't find sub &1()
    &{$testinst->run_scenario}();

    # Errors with "Not a CODE reference"
    &{$testinst}->run_scenario();

    $sub_ref = "$testinst->run_scenario" ;
    &{$sub_ref}(); # Undefined sub t2=HASH(0x....)
I successively moved the trailing '}' around to see the outcome. Nothing. However this one did work
Code:
   $sub_ref = "t2::run_scenario" ;
    &{$sub_ref}() ;
My usage of the &{$sub_ref} suggestion is being used through a routine of mine that is effectively acting as a server and processing requests from satellite programs (it works for me, your results may vary kind of thing)

It is difficult for me to speculate on any possible algorithms that may work for you as I don't know what programming requirements your code has. If the $sub_ref idea is legal, then $sub_ref = "asub" . $i_defined ; &{$sub_ref}(); would possibly be valid as well and that would be tough to find without executing it. If you're into self abuse, write a routine that will read in 'test/t2.pm', alter any sub call to
Code:
if (! exists &($sub_ref}) { print "bad sub>$sub_ref<" . __LINE__ . "\n";} &{$sub_ref}() ;"
, then write that out (eg. test/t2_modified.pm), then require and execute the file you modified. If nothing prints, then 'test/t2.pm' is clean. Recognizing the sub_refs will be the tricky part if the users don't always follow sub calls with () and/or don't precede them with &
 
Never used [tt]bless[/tt] either, but, as
[tt]eval {$testinst->run_scenario()};[/tt]
works for you, the equivalent without eval would be
[tt]$testinst->run_scenario();[/tt]
not
[tt]&{$testinst->run_scenario()};[/tt]
and anyway the latter is incorrect, as the correct syntax is
[tt]&{$var_containing_function_name}();[/tt]
However you have also another possibility
perlfunc said:
You may also use defined(&func) to check whether subroutine &func has ever been defined. The return value is unaffected by any forward declarations of &func. Note that a subroutine which is not defined may still be callable: its package may have an AUTOLOAD method that makes it spring into existence the first time that it is called -- see the perlsub manpage.
After [tt]require[/tt] the subs in the required module are all defined.

Franco
: Online engineering calculations
: Magnetic brakes for fun rides
: Air bearing pads
 
Thank you, Pinkey & prex1, so very much!!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top