Can't find exported subroutine in Perl - perl

I have a module called Utilities.pm. It exports a subroutine called dummy_method.
package Foo::Utilities;
use strict;
use vars qw($VERSION #ISA #EXPORT #EXPORT_OK);
require Exporter;
#ISA = qw(Exporter);
#EXPORT = qw(dummy_method);
sub dummy_method {
# do things
}
I have a Perl script that uses the dummy_method subroutine:
use strict;
use warnings;
use Foo::Utilities qw('dummy_method');
my $foo = Foo::Utilities::dummy_method("foo");
print("$foo\n");
Executing that script throws an export error:
"dummy_method" is not exported by the Foo::Utilities module
Can't continue after import errors at /home/me/foo.pl line 3
BEGIN failed--compilation aborted at /home/me/foo.pl line 3.
I'm confused because I am explicitly exporting that subroutine with #EXPORT = qw(dummy_method);. How do I use dummy_method in another script?

Some people are obsessed with using qw for import lists, even if there is only one element. I think this makes others think this is a requirement, when it is just one way of making a list.
use Foo::Utilities qw('dummy_method');
says to import a method called 'dummy_method', not dummy_method, just like print qw('dummy_method') prints 'dummy_method', not dummy_method.
Try instead:
use Foo::Utilities 'dummy_method';
or, if you must:
use Foo::Utilities qw(dummy_method);
Though since you are exporting it by default, you could just do:
use Foo::Utilities;
Or, since you are calling it as Foo::Utilities::dummy_method, not even export it by default:
use Foo::Utilities ();

The code you have written, with the modification suggested by ysth, works correctly. The only remaining possibility that I can think of is that you have named or located your module incorrectly
use Foo::Utilities 'dummy_method'
will load a file called Foo/Utilities.pm where the Foo directory is in one of the paths in your #INC. It is a common error to omit the initial directories that must be in the path to the module, and you do say that your module is called just Utilities.pm
There must also be a Foo/Utilities.pm that behaves differently, otherwise the use statement would fail even to find the file
I have written your code in more modern Perl. This also works
Foo/Utilities.pm
package Foo::Utilities;
use strict;
use warnings 'all';
use Exporter 'import';
our #EXPORT = qw(dummy_method);
sub dummy_method {
print "dummy_method()\n";
'do things';
}
main.pl
use strict;
use warnings;
use Foo::Utilities 'dummy_method';
my $foo = dummy_method('foo');
print("$foo\n");
There is no need for using vars any more, and it has been better to import the import method from Exporter (instead of inheriting it) since Perl v5.8.7

Related

Program unexpectedly reports "Use of uninitialized value"

This is with regard to my previous question
Hold Subroutine response and set to variable in Perl
The statement Module::thesub("hello")
worked in Module.pm but fails if I move it to main.pl
main.pl
#!/usr/bin/perl
use strict;
use warnings;
use Module;
Module::thesub("hello");
Module.pm
#!/usr/bin/perl
use strict;
use warnings;
package Module;
sub thesub {
state $stored;
$stored = shift if #_;
return $stored;
}
my $testvar = thesub();
print $testvar;
1;
I get this error
Use of uninitialized value $testvar
which means the variable $testvar has no value.
How can I fix this?
Statements in a module run when the module is loaded, at use Module;. At that time thesub() still wasn't called with an argument and $stored in it is undefined. So that is what $testvar gets when it's assigned and the warning is emitted when it's printed.
The $testvar can be used in main
use strict;
use warnings;
use Module;
Module::thesub("hello");
my $testvar = Module::thesub();
even though I am not sure from the question what the purpose of this is.
Remove the assignment and print of $testvar from the module. Note that you'll also need use feature 'state'; at the beginning of the module, to enable the feature pragma.
A few comments on the module you show
No need for #!/usr/bin/perl, as a module is generally not meant to be run
The package Module; is commonly the first line
While what you have works, consider making symbols from the module available to the calling code, so that it can import them and simply say thesub(). A common way is
package Module;
use warnings;
use strict;
use Exporter qw(import);
our #EXPORT_OK = qw(thesub);
sub thesub { ... }
1;
and in the main
use Module qw(thesub);
thesub("hello");
See Exporter for starters and search SO, where there are great many posts on this.

Importing variable into Perl package

I'm writing a basic program whose core logic is split across several project-specific modules for cleanliness (keeping subroutines organised by their purpose in the program's logic).
Suddenly had trouble exposing an option from the main package in one of the modules, and using the our statement appeared to have no effect.
For brevity, I'll copy+paste an isolated test case I wrote to examine this behaviour:
main.pl
#!/usr/bin/perl
use warnings;
use strict;
use File::Basename;
# The variable to be read by the module.
our $verbose = 1;
# Load Output.pm from directory
use lib dirname "$0";
use Output;
write_message "Hello, world\n";
Output.pm
package Output;
use warnings;
use strict;
use parent "Exporter";
our #EXPORT = qw(write_message);
# Should be imported?
our $verbose;
sub write_message {
print $_[0] unless !$verbose;
}
1;
Expected result: "Hello, world"
Actual result: Dead silence
It's quite possible that what I'm trying to achieve isn't even possible in Perl, as this isn't the intended use of modules (and heck, I understand why that'd be the case).
I'm still quite new to Perl and there are some things I'm struggling to wrap my head around. I've seen people recommend using the our declarator to expose a variable across packages, but I can't understand why this isn't working.
PS: If anybody knows a better approach to splitting an app's program-specific logic between modules, I'd appreciate some pointers too. :) But first and foremost, I'd prefer to learn why our-ing a variable isn't working.
An our statement just creates a package variable (whereas my creates a lexical variable). It has nothing to do with exporting
The best option is probably to declare the variable in the Output package and access it as $Output::verbose elsewhere. Like this
main.pl
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
use lib dirname $0;
use Output;
$Output::verbose = 1;
write_message "Hello, world\n";
Output.pm
package Output;
use strict;
use warnings;
use Exporter 5.57 'import';
our #EXPORT = qw/ write_message /;
our $verbose;
sub write_message {
print $_[0] if $verbose;
}
1;
Note that I have also removed the incorrect quotes from around $0, and ever since version 5.57 of Exporter it has been possible (and preferable) to import it's import subroutine instead of subclassing it
our declares a package variable in the current package. The one in main.pl refers to $main::verbose; the one in Output.pm refers to $Output::verbose.
You can use the full name $main::verbose to access the variable from anywhere, but you can't really "export" it because exporting refers to making symbols accessible to users of your module. You're trying to do the opposite.

Perl: can you make a "use"d module or setting apply to the including script?

For example, every single one of my scripts has this at the top:
use warnings; use strict; use v5.10;
use Tools;
My "Tools" package has a bunch of functions that I use all the time. I would rather just include it and have it use warnings, strict, and 5.10 for the including script, since I have to use those for every script anyway. Is there a way to do that?
You can use Import::Into with a custom import method to turn stuff on in the importing class. For example:
package Tools;
use strict;
use warnings;
use feature ':5.10';
use Import::Into;
sub import {
my $target = caller;
strict->import::into( $target );
warnings->import::into( $target );
feature->import::into( $target, ':5.10' );
# other imports, etc
}
I wrote a more detailed post about using Import::Into here: Removing Perl Boilerplate with Import::Into.

import a library subroutine while using FindBin in perl

EDIT
Sorry for the confusion, here is my updated question.
I am using FindBin in my perl script like this:
use FindBin qw($Bin);
use lib "$Bin/../lib";
use multi_lib qw(say_hello_world);
This works:
multi_lib::say_hello_world();
but this does not:
say_hello_world();
EDIT 2
This is how multi_lib.pm looks:
package multi_lib;
use strict;
use warnings;
use 5.010;
require Exporter;
my #ISA = qw(Exporter); # removing `my` causes an error!
my #EXPORT_OK = qw(say_hello_world); # removing `my` causes an error!
sub say_hello_world {
say "hello world!";
}
p.s.
I have no idea what does #ISA stand for and if adding the my is OK... I followed the preldoc for Exporter.
Edit 3
I think I solved it by moving #EXPORT_OK before use strict. I am used to put use strict right at the beginning of my scripts but I guess this is not the way to go here (?). Anyway, this works:
use Exporter 'import';
#EXPORT_OK = qw(say_hello_world);
use strict;
...
I would still appreciate some explanations as to what exactly is going on here and what is the recommended way of exporting subroutines (like I did?).
You can't do that. lib's import() routine modifies #INC instead of trying to export anything.
But in any case, there are no functions in lib.pm that are suitable for external use. What are you really trying to accomplish?
Updated answer for updated question:
No, you cannot use my() on #EXPORT_OK; it needs to be globally visible so Exporter can use it.
Say our #EXPORT_OK; instead. The same is true for #ISA; the package variable #ISA controls inheritance, a lexical #ISA does nothing. I prefer not inheriting from Exporter, though; you do this (except with very old Exporter) by just importing Exporter's import routine:
use Exporter 5.57 'import';
The error you got that prompted you to add my() was because you specified use strict; (which, among other things, requires that variables be properly declared unless they are package variables qualified by package name or special global variables). our() is the equivalent to my() that declares variables as package variables instead of lexicals, so they are accessible from outside the scope in which they are declared. It's better to properly declare them with our() than to just move them above use strict; to get around the error.
That's not the way libraries work. You need to set your library location then load a module (.pm) from it that contains the subroutine you want.
I would like to imprt a specific
subroutine (aka say_hello_world) from
lib, but this does not work:
use lib "$Bin/../lib" qw(say_hello_world);
The use lib just points you to the directory where the files are, you need to specify the file as well. If your subroutine is in a file Example.pm then you need
use Example qw(say_hello_world);
Also note that the FindBin part needs to be inside a BEGIN block:
BEGIN {
use FindBin qw($Bin);
use lib "$Bin/../lib";
};
use Example qw(say_hello_world);

Why can't my Perl script see the our() variables I defined in another file?

I have a question relating to Perl and scoping. I have a common file with lots of various variables. I require the common file in my main script, but I cannot access the variables; they seem to be outside of its scope. I assumed that an our declaration would overcome that problem, but it doesn't seem to work.
Script 1: common.pl
#!/usr/bin/perl
our $var1 = "something";
our $var2 = "somethingelse";
Script 2: ftp.pl
#!/usr/bin/perl
use strict;
use warnings;
require('common.pl');
print $var1;
I get the error: Global symbol "$var1" requires explicit package name
There's no require statement in your second example, but it wouldn't work anyway. What our does is declare a lexically-scoped package variable. Since you have no package statement, it uses the default package main. So your first script sets up the variable $main::var1, but this will only be available within that file's scope.
A better way to provide common variables for other scripts is to use Exporter. You can define package symbols in one place and Exporter will take care of copying them to the requesting script or class's namespace when needed.
I would put the config in a module instead.
File: MyConfig.pm
package MyConfig;
require Exporter;
use strict;
our #ISA = qw(Exporter);
our #EXPORT = qw( getconfig );
my %confighash = (
thisone => 'one',
thatone => 2,
somthingelse => 'froboz',
);
sub getconfig {
return %confighash;
}
1;
Example usage:
#!/usr/bin/perl
use strict;
use warnings;
use MyConfig;
my %config = getconfig();
print $config{ somthingelse };
This should print froboz
It looks like you need a proper configuration file there. I'd go for a non-code configuration file that you can read when you need to setup things. There are modules on CPAN to handle just about any configuration format you can imagine.
If you want to do it the way you have it, get rid of our and declare them with use vars. Don't let the PBP police scare you off that. :) You only really need our to limit a scope of a package variable, and that's exactly the opposite of what you are trying to do.
our() does something a little different than you think. Its sole purpose is to work with strict in requiring you to declare package variables that you are going to use (unless they are fully-qualified or imported). Like strict, its effect is lexically-scoped. To use it to allow accessing a global $main:var1 from multiple files (which are separate scopes) as just $var1, you need to say our $var1 in each file.
Alternatively, you would change your required file to be a module with its own package that exports the variables to any package that uses it.
Try this. I am new at Perl but this is how I got it to work on a script I made
#!/usr/bin/perl
$var1 = "something";
$var2 = "somethingelse";
Script 2: ftp.pl
#!/usr/bin/perl
use strict;
use warnings;
our $var1;
our $var2;
require('common.pl');
print $var1;