Re: error for undefined function that's been imported
by rinceWind (Monsignor) on Aug 15, 2005 at 09:57 UTC
|
Seems to work OK for me. I've never had any problems with this. Could you post some code, particularly how @EXPORT and friends are populated.
Just knocked up the following to prove that export behaves as expected: (note that I have not called the packages A and B because of a potential namespace clash with B::)
Alpha.pm
package Alpha;
use strict;
BEGIN {
use Exporter ();
use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 0.01;
@ISA = qw (Exporter);
@EXPORT = qw (foo);
@EXPORT_OK = qw (foo);
%EXPORT_TAGS = ( all => [qw/foo/]);
}
sub foo {
print "Working\n";
}
1;
Bravo.pm
package Bravo;
use strict;
use Alpha;
BEGIN {
use Exporter ();
use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 0.01;
@ISA = qw (Exporter);
@EXPORT = qw (bar);
@EXPORT_OK = qw (bar);
%EXPORT_TAGS = ( all => [qw/bar/]);
}
sub bar {
foo();
}
1;
Executing the command perl -MBravo -e "bar()" outputs the string "working".
--
Oh Lord, won’t you burn me a Knoppix CD ?
My friends all rate Windows, I must disagree.
Your powers of persuasion will set them all free,
So oh Lord, won’t you burn me a Knoppix CD ? (Missquoting Janis Joplin)
| [reply] [d/l] [select] |
|
|
Sure-- this stuff works ok almost all the time. The sporadic behavior is one that is hard to describe, really. But, to answer your question, here's some code: (note the inclusion of the Register module)
MyUserProfile.pm
package MyUserProfile;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(ProfileGet ProfileSave ProfileEdit ProfileUpdate
ProfileFix ProfileExtract ProfileFixContactInf
+o
$user_data $contact_strings, @contact_info);
our $VERSION = 1.00;
use MyErrors;
use CGI;
use Register qw(PrintTemplate RegisterChange RegisterAcctPath Register
+SetupUser
EDIT_PROFILE VIEW_IMAGES read_user_db);
use MyUtils qw(:DEFAULT);
Now, here's a snippet from Register:
Register.pm
[...]
use MyUserProfile qw(:DEFAULT);
[...sometime later in some function...]
my $acct_data = MyUserProfile::ProfileGet($acct);
[...]
Even though ProfileGet() is properly exported, I can't just call it without perl complaining about Register.pm calling an undefined function. I have to use it in the form above to get it to shup up (and work). But this is the frustrating part: it's not ALWAYS the case. Sometimes, functions can be called directly, whereas other times, maybe not. Maybe there's a pattern to when it works and when it doesn't, but suffice to say, I can't see it (hence, the appearance of randomness).
Could the inter-dependence on the modules to one another be causing this, or point to the direction of the problem?
| [reply] [d/l] [select] |
|
|
What you have should, on the surface, be working.
:DEFAULT should be doing what it says on the tin, but try explicitly naming the subs you wish to import, and see if this works.
One point that occurs to me is that Register.pm could be switching namespaces - with further package declarations. If this is the case, the use will import the subs once - into the package namespace of its context. This is why you need to declare your package first before using Exporter style modules. OO modules don't suffer from this problem as they use a different technique for getting to the namespace.
If you've still got a problem, I suggest dumping out the key stashes (symbol table hashes) after BEGIN has happened, to see whether Exporter has aliased the subs properly. One way to do this is with the debugger (perl -d) - run it up and issue the command:
DB<1> x %Register::
and see if the sub names appear.
Alternatively, add the following lines to your main program: use Data::Dumper;
print Dumper \%Register::;
--
Oh Lord, won’t you burn me a Knoppix CD ?
My friends all rate Windows, I must disagree.
Your powers of persuasion will set them all free,
So oh Lord, won’t you burn me a Knoppix CD ? (Missquoting Janis Joplin)
| [reply] [d/l] |
|
|
|
|
|
|
|
Could the inter-dependence on the modules to one another be causing this
This is all a guess, but...
Yes. Specifically, the subs you're trying to import (and the @EXPORT list, and I think &import itself for that matter) don't actually exist when import is called. Since use effectively runs inside a BEGIN block, the use Register line interrupts compilation of MyUserProfile to execute Register.pm. Then while compiling Register, the use MyUserProfile line will first attempt to require MyUserProfile, which is a no-op since it is already in %INC even though it hasn't finished compiling, and then it will import MyUserProfile, which is also a no-op since nothing in that package is defined yet (except what was exported to it by earlier use statements).
By the way, that's for the case where a script tries to use MyUserProfile without first doing a use Register. It looks like there are similar problems (with different functions) going the other way. Hence it "seems to happen in parts of the code that I'm NOT working on".
| [reply] [d/l] [select] |
|
|
our %EXPORT_TAGS =
( :DEFAULT => [ qw( ProfileGet ProfileSave ProfileEdit ) ] );
Update: Thanks to ysth and rinceWind for the whack with the clue by four.
| [reply] [d/l] |
|
|
|
|
|
|
I'm replying to my own posting to provide an update with a new observation. In my attempts to understand rinceWind's description of how use/import works by playing with the order of which packages are imported with use (and getting absolutely nowhere), I decided to re-order the use statements in the actual perl program itself--not in the modules. To my surprise (because I don't understand it), the problem magically went away. Yes, by merely swapping which package was included first, the error evaporated:
use MyUserProfile "ProfileGet";
use Register qw(PrintTemplate);
This works. Swap the two, and I get the undefined function error for MyUserProfile.pm. Problem is, this "solution" only happens to work with this perl script. I can write another that merely makes different calls to some other functions in these packages, and I'll get another "undefined function" error... which, again, can be fixed by--sigh--swapping the use statements in that perl script.
So, what I'm left with is a complete misunderstanding of how to generally avoid such a problem with any given perl script that imports these (and many other) packages (that I write). I've got a ton of routines in each package that all call routines into each other. I don't mind following the rules about order and precedence, but I either don't know what they are (I thought I did), or the speghetti just gets too tangled when module sizes (and quantities of them that use each other) exceeds a certain threshold.
| [reply] [d/l] [select] |
|
|
I'm not sure whether you've benefitted as much as you'd like from rincewind's discussion, but in my own (less than fully experienced) opinion, Bob9000's point is the more pertinent one. If I see a code base being built up with packages like this:
## file: SomeModule.pm ##
package SomeModule;
#...
use OtherModule;
#...
__END__
## file: OtherModule.pm ##
package OtherModule;
#...
use SomeModule;
#...
my first reaction is "don't do that", and my next reaction is "this is crying out desparately for refactoring".
Cross-references like that turn your code into a sort of "chicken-and-egg" problem -- if not in terms of actual compilation troubles, then at least in terms of ongoing maintenance. When someone else comes along to fix or add something, they have to go around in circles to track what the code is doing (and that "someone else" could very well be you, six months from now).
The main point of creating modules is to encapsulate things that are logically independent of other things. Having mutual dependencies among modules just defeats their purpose.
Look at the functions defined in "MyUserProfile.pm" that are called from "Register.pm", and likewise at those defined in the latter that are called from the former. If you can put these functions into one or more distinct modules, and have the two original modules use the new ones instead of using each other, things will be easier all around.
Ideally, this should lead you in the same direction that rincewind suggested: an OO design. If a current module is based on certain data being shared by its various subs, you have an obvious basis for making that into an object; if not, then just dividing up the subs a bit differently to eliminate module cross-dependencies should be fairly easy and sufficient, and will reduce incoherence in your code. | [reply] [d/l] |
|
|