Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Constants in Perl

Tags:

constants

perl

I am trying to define constants in Perl using the constant pragma:

use constant {
    FOO => "bar",
    BAR => "foo"
};

I'm running into a bit of trouble, and hoping there's a standard way of handling it.

First of all...

I am defining a hook script for Subversion. To make things simple, I want to have a single file where the class (package) I'm using is in the same file as my actual script.

Most of this package will have constants involved in it:

 print "This is my program";

 package MyClass;

 use constant {
    FOO => "bar"
 };

 sub new { ... }

I would like my constant FOO to be accessible to my main program. I would like to do this without having to refer to it as MyClass::FOO. Normally, when the package is a separate file, I could do this in my main program:

use MyClass qw(FOO);

but, since my class and program are a single file, I can't do that. What would be the best way for my main program to be able to access my constants defined in my class?

The second issue...

I would like to use the constant values as hash keys:

$myHash{FOO} = "bar";

The problem is that %myHash has the literal string FOO as the key and not the value of the constant. This causes problems when I do things like this:

if (defined $myHash{FOO}) {
   print "Key " . FOO . " does exist!\n";
}

I could force the context:

if (defined $myHash{"" . FOO . ""}) {

I could add parentheses:

if (defined $myHash{FOO()}) {

Or, I could use a temporary variable:

my $foo = FOO;
if (defined $myHash{$foo}) {

None of these are really nice ways of handling this issue. So, what is the best way? Is there one way I'm missing?

By the way, I don't want to use Readonly::Scalar because it is 1). slow, and 2). not part of the standard Perl package. I want to define my hook not to require additional Perl packages and to be as simple as possible to work.

like image 506
David W. Avatar asked Jun 16 '10 20:06

David W.


1 Answers

If you want to keep everything in the same file, you can define your constants package as follows:

use warnings;
use strict;

BEGIN {  # BEGIN means this will all happen at compile time
    package Constants;

    $INC{'Constants.pm'}++;     # tell `require` that the package is loaded
    use base 'Exporter';        # setup package to export
    our @EXPORT_OK = qw( PI );  # what to export

    use constant PI => 3.14159; # define your constant
}

package main;

use Constants qw( PI );  # use it like normal

print PI;

Then to fool the auto-quoting inside hash subscripts, you can write it like this: $hash{+PI} or $hash{(PI)} or $hash{PI()} or $hash{&PI} or $hash{::PI} ... I could probably keep going, but I think you get the point.

The reason that $INC{'Constants.pm'}++ is needed is because the use Constants qw( PI ); line really means:

BEGIN {
    require 'Constants.pm';
    Constants->import( qw( PI ) );
}

And require will check %INC to see if the package has been loaded already. So by giving it a true value (1 in this case), the require 'Constants.pm'; portion of the use will become a no-op.

like image 173
Eric Strom Avatar answered Oct 22 '22 10:10

Eric Strom