相关文章推荐
完美的苦瓜  ·  Pcap_loop函数-CSDN博客·  6 月前    · 
文雅的煎饼果子  ·  web3.eth.accounts_web3 ...·  1 年前    · 
大鼻子的小虾米  ·  Spark ...·  1 年前    · 
严肃的烤土司  ·  C++ 如何去掉 string ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

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.

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.

@David W. => Glad to help and welcome to SO. Also, so you know use constant PI => 3.14159 means the same thing as sub PI {3.14159} and I usually use the latter because its a little shorter and doesn't mislead one to think its something other than a sub. – Eric Strom Jun 16, 2010 at 21:51 Note that if you prefer not to mess with %INC, you can use BEGIN { MyClass->import(qw(FOO)) } instead of use MyClass qw(FOO); (after setting MyClass up to use Exporter in the usual way). – cjm Jun 16, 2010 at 21:58 I know this is really old, but does Constants.pm have to export anything for this to work? – qodeninja Aug 29, 2011 at 23:21
  • Perl constants are not constants. They are compile-defined to be a particular kind of function that is inlined at compile-time.

  • I find that Perl 'constants' are more a pain to use than to not use. So, typically my approach is to use scalars with all uppercase. my $PI = 3.14159;.

  • You should make those scalars Readonly, or someday you'll be wondering why your circles are coming out square. – Ether Jun 16, 2010 at 21:24 @Ether: A square is an digitized approximation of a circle - just doesn't have very high resolution. :) – Paul Nathan Jun 16, 2010 at 21:36 If I had enough of a reputation, I'd vote this down. Good programming practices require the use of constants. If you don't like the "use constant" pragma, use the "Readonly" modules, or do it the old fashion typeglob way: *FOO = \"bar"; our $FOO; print "Value of FOO is $FOO\n"; #Just like a scalar variable $FOO = "barfoo"; #Can't do this! Readonly isn't a standard module and can be slow. Typeglobbing is fast, but the syntax is weird. – David W. Jun 16, 2010 at 21:39 @David: Yeah, constants in perl are sorta broken. So I do disciplined programming to deal with the problem. – Paul Nathan Jun 16, 2010 at 21:45 @dolmen Unfortunately, there's are two problems with Readonly 1. It's not part of the standard Perl package. Therefore, I have to depend upon the site to download and install which might not always be possible. 2. It's slow -- especially if you don't use the XL package with it. Otherwise, I prefer the way Readonly works vs. the use Constant. In fact, for the longest time, I preferred typeglobbing: *PI = \3.1415926; our $PI; This would allow me to treat constants as variables, but I still can't modify them. Unfortunately, the Perl community went with Use Constant – David W. Jun 18, 2010 at 1:05

    Barewords are automatically quoted when they appear in a hash lookup. You need to force the sub that implements the constant to be invoked instead:

    $myHash{FOO}   = 'bar'; # doesn't work, bareword quoted
    $myHash{+FOO}  = 'bar'; # okay
    $myHash{&FOO}  = 'bar'; # okay
    $myHash{FOO()} = 'bar'; # okay
    

    Exporting functions and variables from one package to another is all symbol table manipulation. The Exporter module makes this easy for us, but it is not too hard to do it without a module.

    package main;
    sub stuff { return &FOO x 3 }
    *FOO = *MyClass::FOO;
    print stuff();               # "barbarbar"
    package MyClass;
    use constant FOO => "bar";
    

    You could also have said

    BEGIN { *main::FOO = *FOO }
    

    under package MyClass.

    Note: Wrapping the glob assignment in a BEGIN block is necessary to make strict 'subs' happy. – Michael Carman Jun 16, 2010 at 21:37 Thanks for the answer. Yes, I figured out about the unitary operator (+) which looks the best. I didn't think about symbol table modifications. That'll work. If I had enough of a reputation, I'd vote this answer up. – David W. Jun 16, 2010 at 21:41

    I second Eric Strom answer.

    However, here is a an other way (but that exposes too much the Perl implementation):

    use strict;
    use warnings;
    package Constants;
    sub FOO() { 'bar' }
    sub BAR() { 'foo' }
    sub main::FOO() { FOO }
    sub main::BAR() { BAR }
    package main;
    print FOO, BAR, "\n";
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.