1
votes

I'm not sure how to call another subroutine from a sub declared in a perl module. The subroutine is simply getting date information.

I have a file handle open for a log file and I want to print out some date information to this logfile, but its not printing out. I can see that it touches the file, I made the log file -rwx too.

Here is the sample subroutine in perl (Test.pm) module that I am calling:

sub spGetCurDateTime {
    my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
    my $currentDateTime = sprintf "%4d-%02d-%02d %02d:%02d:%02d",
        $year+1900, $mon+1, $mday, $hour, $min, $sec;
    return $curDateTime;
}

Here's part of my test code where I am trying to call the subroutine from (Test.pm)

use Test.pm;

#Create log
open (LOG, ">/home/dev/test.log") || die "cannot append";

sub get_alert {
   undef $/;
   open (my $QFH, "< /home/dev/test.sql") or die "error can't open this file $!";
   print LOG "Checking on status time =>", &spGetCurDateTime, "\n";
   my $sth= $dbh->prepare(<$QFH>) ||
      die ("Cannot connect to the database: ".$DBI::errstr."\n");
   $sth->execute;
   close $QFH;
   my $row = $sth->fetchrow_hashref;
   $sth->finish;
   return $row->{RESULT};
   print $row;
}
3
Use POSIX::strftime instead? - TLP
@TLP there is a downside to POSIX::strftime, it is half the speed of sprintf. - Chas. Owens
@Chas Are you saying that we should not use this module? - TLP
@TLP No, just be aware of the benefits and drawbacks of it. strftime does a lot more than sprintf does. For instance, you can say my @time = localtime; $time[2] += 24; print strftime "%F %T\n", @time;. If you were to try that with sprintf you would get an invalid date. Basically, if all you want to do is format localtime as it comes back, then sprintf may be a better choice, especially if it is in a function that gets called a lot. If you need to normalize a time or provide a nice interface to someone else, strftime is your friend. - Chas. Owens
what is sprintf better for, besides running your benchmark code in 5 microseconds instead of 9? - ysth

3 Answers

5
votes

First, never say &functionname unless you know why you might want magic behavior. In general in Perl 5, function calls look like functionname() or functionname depending on whether you want it treated as a term (i.e. the highest level of precedence) or an operator (it is easier when you are starting out to just always use the functionname() form since that will do what you expect it to).

You can say

print LOG "Checking on status time =>", Test::spGetCurDateTime(), "\n";

But that can get old, so most people use the Exporter module to export certain functions and variables into the namespace that said use Test;. You may also wish to avoid using the module name Test, there is already a module with that name.

In the file T.pm:

package T;

use strict;
use warnings;

use Exporter qw/import/;
our @EXPORT_OK = qw/spGetCurDateTime/;

sub spGetCurDateTime {
    my ($sec, $min, $hour, $mday, $mon, $year) = localtime;
    my $currentDateTime = sprintf "%4d-%02d-%02d %02d:%02d:%02d",
        $year+1900, $mon+1, $mday, $hour, $min, $sec;
    return $currentDateTime;
}

1;

In the file t.pl:

#!/usr/bin/perl

use strict;
use warnings;

use T qw/spGetCurDateTime/;

print spGetCurDateTime(), "\n";
0
votes

Read man perlmod. You need to put the function as an exported symbol, fully qualify it, or import it specifically...

0
votes

One of the things many users don't understand about Perl is namespace.

Without handling namespace, something like the CPAN archive of Perl modules would be impossible because each and every module would have to make sure that no other modules have subroutines and variable names that any other module has.

In the standard way Perl is written, each Perl module has its own namespace. That way, variable names and functions don't collide with variables and functions you have defined in your program.

Take a look at your Test module, and see if there's a line:

package Test;

in the top of the program. If it does, it means each variable in your module and each subroutine is in the namespace of Test and not your main namespace (which is the default namespace). The package command changes the namespace until the next package command.

If, (as we suspect), your Test.pm module has the package command, you'll need to refer to your spGetCurDateTime subroutine as Test::spGetCurDateTime.

As has already been pointed out, you can import variables and function names into the current namespace by using Exporter.

package Test;
use base qw(Exporter);

our @EXPORT qw(spGetCurDateTime log);

sub spGetCurDateTime {
};

In your main program:

use Test;

will automatically import your spGetCurDateTime and log subroutines, and you can simply use them as spGetCurDateTime() and log instead ofTest::spGetCurDateTimeandTest::log`.

However, default importing of all subroutines and variables is now discouraged since it can interfere with the user's own program without warning. In the above example, I'm not only importing the log function, but I am also overriding the log function that's built into Perl (log takes the natural logarithm of a number).

Better, is to use EXPORT_OK and let users select the functions they want to import:

package Test;
use base qw(Exporter);

our @EXPORT_OK qw(spGetCurDateTime log);

sub spGetCurDateTime {
};

In your main program:

use Test qw(spGetCurDateTime);

Now, you can do spGetCurDateTime() instead of Test::spGetCurDateTime, but you haven't overridden log with out realizing it. You can still say Test::log to execute the log subroutine in your Module.