"Undefined subroutine" error (but it's defined, I think?)

V

valerian2

I have a program divided into 3 modules plus a main script, and some of
the modules call functions within other modules. Most of the time this
works fine, but occasionally I get an error like this:
Undefined subroutine &My::Misc::DB_Disconnect called at My/Misc.pm line 18.
even though I made sure the caller (here Misc.pm) had imported all the
functions of the other module. Hence, I don't understand the reason
behind this error. I can get it to work fine by just replacing the
DB_Disconnect() call in Misc.pm with &My::Misc::DB_Disconnect(), but
that seems like a kludge, and I'd like to get a better understanding of
what's going on. :)

Here's what happens when I run the program (under perl 5.6.1):
$ ./test.pl
Error: /dev/null is full!
Disconnecting from DB...
Undefined subroutine &My::Misc::DB_Disconnect called at My/Misc.pm line 18.

I isolated the problem to the "use My::SOAP;" line in DB.pm, but I need
it there because some of the DB functions operate on SOAP data. So I
left it there and poked around some more...

I then isolated the real problem to be the "use My::Misc;" line in
SOAP.pm, but again I need that line there, because the SOAP subs call
a general untainting function in Misc.pm before returning the data.

Here's the four files in question (I deleted everything but the bare
minimum to reproduce this behavior):


----- ~/test.pl ---------------------------------
#!/usr/bin/perl -w

use strict;
use My::DB;
use My::Misc;

my $dbh = DB_Connect();
SafeError($dbh, '/dev/null is full!');


----- ~/My/DB.pm --------------------------------
# Database functions
package My::DB;

use strict;
use Exporter;
use My::SOAP; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(DB_Connect DB_Disconnect);

sub DB_Connect {
# stub function, always returns true
my $dbh = 1;
return ($dbh);
}

sub DB_Disconnect {
# stub function, always returns true
my ($dbh) = @_;
return (1);
}

1;


----- ~/My/Misc.pm ------------------------------
# Miscellaneous functions (error handling, logging, untainting, etc.)
package My::Misc;

use strict;
use Exporter;
use My::DB;
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(SafeError);

sub SafeError {
my ($dbh, $msg) = @_;

print "Error: $msg\n";

if ($dbh) {
print "Disconnecting from DB...\n";
DB_Disconnect($dbh);
}

exit 1;
}

1;


----- ~/My/SOAP.pm ------------------------------
# SOAP functions, for exchanging data with remote site
package My::SOAP;

use strict;
use Exporter;
use My::Misc; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(GetRemoteData);

sub GetRemoteData {
# stub function, always returns true
return (1);
}

1;
 
B

Bob Walton

I have a program divided into 3 modules plus a main script, and some of
the modules call functions within other modules. Most of the time this
works fine, but occasionally I get an error like this:

even though I made sure the caller (here Misc.pm) had imported all the
functions of the other module. Hence, I don't understand the reason


No you didn't. See comments at end.

behind this error. I can get it to work fine by just replacing the
DB_Disconnect() call in Misc.pm with &My::Misc::DB_Disconnect(), but
DB------------------------------------------^^^^


that seems like a kludge, and I'd like to get a better understanding of
what's going on. :) ....
----- ~/test.pl ---------------------------------
#!/usr/bin/perl -w

use strict;
use My::DB;
use My::Misc;

my $dbh = DB_Connect();
SafeError($dbh, '/dev/null is full!');


----- ~/My/DB.pm --------------------------------
# Database functions
package My::DB;

use strict;
use Exporter;
use My::SOAP; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(DB_Connect DB_Disconnect);

sub DB_Connect {
# stub function, always returns true
my $dbh = 1;
return ($dbh);
}

sub DB_Disconnect {
# stub function, always returns true
my ($dbh) = @_;
return (1);
}

1;


----- ~/My/Misc.pm ------------------------------
# Miscellaneous functions (error handling, logging, untainting, etc.)
package My::Misc;

use strict;
use Exporter;
use My::DB;
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(SafeError);

sub SafeError {
my ($dbh, $msg) = @_;

print "Error: $msg\n";

if ($dbh) {
print "Disconnecting from DB...\n";
DB_Disconnect($dbh);
}

exit 1;
}

1;


----- ~/My/SOAP.pm ------------------------------
# SOAP functions, for exchanging data with remote site
package My::SOAP;

use strict;
use Exporter;
use My::Misc; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(GetRemoteData);

sub GetRemoteData {
# stub function, always returns true
return (1);
}

1;

Note in:

perldoc -f use

that use() calls require(). Note in

perldoc -f require

that require() keeps track of which modules have been loaded in %INC (a
global variable, always in package main). Therefore, when the use()
function is called, it will do something only if that module has not
already been loaded. So each module will be loaded exactly once.
Subsequent calls to use() will not do anything. Therefore, when you use
Exporter; to export your sub's, it only happens during the first use()
of your module. Thus, the export of your sub names happens only the
first time a given modules is use()'d.

Basically, you are misusing (pun intended) the module mechanism in an
attempt to simply define subs. If all you want is for all your subs to
be present even though they are defined in other files, simply do() the
other files, and forget modules and packages. When you properly use
modules, you do not share subs (in modules, properly called methods).

From the docs for Exporter:

"Do not export method names!

Do not export anything else by default without a good reason!

Exports pollute the namespace of the module user. If you must
export try
to use @EXPORT_OK in preference to @EXPORT and avoid short or common
symbol names to reduce the risk of name clashes."

You are trying to use use() wrong. Read up on the various
object-oriented docs and buy "Object Oriented Perl" and read it.
 
V

valerian

No you didn't. See comments at end.

Alright, I went and spent some intimate time with 'man perlmod', 'man
perlmodlib', 'man Exporter', 'perldoc use', 'perldoc require', 'perldoc
do', and all their friends... :)

But none of those explain why the 'use DB;' line in Misc.pm is not
importing the functions in the @EXPORT of DB.pm. The only way I can
get this to work right is either by fully qualifying the function
name (&My:DB:DB_Disconnect) or by inserting the line 'import My::DB;'
somewhere in the My::Misc::SafeError sub. But adding the import line
outside that scope doesn't work. That's what I don't understand. That
sub belongs to the My::Misc package, so why doesn't it honor the pragmas
in that package?

And to make it even more confusing, in the 'real' modules (not the
skeleton ones I listed previously) there are some subs that do honor the
package pragmas, and some that don't. In both cases I'm talking about
subs in the same package making identical calls to the same function in
another package. Frex, in My::DB, several subs call My::Misc::GetDate,
and in some cases I have to fully qualify it, and in some cases I don't.
I can't seem to find a pattern though. :-(
Note in:

perldoc -f use

that use() calls require(). Note in

perldoc -f require

that require() keeps track of which modules have been loaded in %INC (a
global variable, always in package main). Therefore, when the use()
function is called, it will do something only if that module has not
already been loaded. So each module will be loaded exactly once.
Subsequent calls to use() will not do anything. Therefore, when you use
Exporter; to export your sub's, it only happens during the first use()
of your module. Thus, the export of your sub names happens only the
first time a given modules is use()'d.

I rather like having everything exported as soon as possible. It's a
mod_perl app, with all the modules preloaded by startup.pl, to avoid as
many delays as possibly...

I'm not sure what difference it makes that %INC is a global var though.
That shouldn't negatively affect My::Misc, as the 'use DB;' line would
just cause the 'require' step to be skipped and simply go on to import
the @EXPORT subs in My::DB from the copy already in memory, right?
Basically, you are misusing (pun intended) the module mechanism in an
attempt to simply define subs. If all you want is for all your subs to
be present even though they are defined in other files, simply do() the
other files, and forget modules and packages. When you properly use
modules, you do not share subs (in modules, properly called methods).

Well I only have a rudimentary understanding of OO Perl (having read the
introductory OO chapter in Advanced Perl Programming--which incidentally
I found easier to follow than the stuff in the camel book), so I'm not
quite ready to take the plunge yet. And besides, I've already got well
over 10,000 lines of procedural code that I have to finish testing ASAP,
so there's no time to start tearing things up. Maybe next project... :)

Still, I don't think it's a total waste to use modules for just plain
old procedural code. I make judicious use of @EXPORT, @EXPORT_OK and
use specific sub names based on the package name to avoid namespace
clashes (frex, DB_Disconnect and other DB_ prefixed subs are in the
My::DB package).
 
B

Benjamin Goldberg

package My::DB;

use strict;
use Exporter;
use My::SOAP; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(DB_Connect DB_Disconnect); [snip function definitions]
# SOAP functions, for exchanging data with remote site
package My::SOAP;

use strict;
use Exporter;
use My::Misc; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(GetRemoteData);
[snip function definition]

Remember that 'use' happens at compile time, and @EXPORT= and @ISA=
happen at run time.

If you changed the beginnings of these three modules to:

package My::DB;
BEGIN {
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(DB_Connect DB_Disconnect);
}
use strict;
use My::SOAP;

And:

package My::SOAP;
BEGIN {
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(GetRemoteData);
}
use strict;
use My::Misc

And:

package My::Misc;
BEGIN {
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(SafeError);
}
use strict;
use My::DB;

Then it *should* work. [but since this code is untested, don't sue me
if it doesn't.]
 
B

Bob Walton

Benjamin said:
package My::DB;

use strict;
use Exporter;
use My::SOAP; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(DB_Connect DB_Disconnect);
[snip function definitions]
# SOAP functions, for exchanging data with remote site
package My::SOAP;

use strict;
use Exporter;
use My::Misc; # XXX this line causes the problem
use vars qw(@ISA @EXPORT);
@ISA = ('Exporter');
@EXPORT = qw(GetRemoteData);
[snip function definition]

Remember that 'use' happens at compile time, and @EXPORT= and @ISA=
happen at run time.

If you changed the beginnings of these three modules to:

package My::DB;
BEGIN {
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(DB_Connect DB_Disconnect);
}
use strict;
use My::SOAP;

And:

package My::SOAP;
BEGIN {
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(GetRemoteData);
}
use strict;
use My::Misc

And:

package My::Misc;
BEGIN {
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(SafeError);
}
use strict;
use My::DB;

Then it *should* work. [but since this code is untested, don't sue me
if it doesn't.]


Hmmmmm...yes, that does make it work. But I don't think the explanation
is quite right. @EXPORT and @ISA *do* get defined at compile time,
because the use() is the same as BEGIN{require module;import module}.
The require() do'es the module code, including the assignments to
@EXPORT and @ISA, at compile time. And the import method runs at
compile time. The do() from the use() will run before the import, so
everything should be defined. BUT some of the modules are use'd twice,
and the second time the do() is not performed. In the package of the
second call to use(), @EXPORT and @ISA don't get defined, so the routine
doesn't know it is an Exporter, so Exporter's import doesn't get called.
And the @EXPORT list doesn't get exported. Your solution works
because @ISA and @EXPORT are defined in the package namespace before the
use() is called. I think that is right because I traced through it with
the debugger with BEGIN{$DB::single=1} to debug the stuff executed at
compile time.

Pretty hairy. My apologies for my incorrect first analysis, as pointed
out by Abigail and valerian2.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top