X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2Fconstant.pm;h=159c29978a596d6eecba75e4cdffda735e5df442;hb=e9a5b206a27d8bea1fef9f43666333f221ba873d;hp=bbfdb78ec408ee6ca696b6c4f0d57e5625e2b5bc;hpb=cb50131aab68ac6dda048612c6e853b8cb08701e;p=p5sagit%2Fp5-mst-13.2.git diff --git a/lib/constant.pm b/lib/constant.pm index bbfdb78..159c299 100644 --- a/lib/constant.pm +++ b/lib/constant.pm @@ -1,10 +1,11 @@ package constant; use strict; -use 5.005_64; +use 5.006_00; +use warnings::register; our($VERSION, %declared); -$VERSION = '1.01'; +$VERSION = '1.05'; #======================================================================= @@ -27,81 +28,88 @@ my %forbidden = (%keywords, %forced_into_main); sub import { my $class = shift; return unless @_; # Ignore 'use constant;' - my $name = shift; - unless (defined $name) { - require Carp; - Carp::croak("Can't use undef as constant name"); + my %constants = (); + my $multiple = ref $_[0]; + + if ( $multiple ) { + if (ref $_[0] ne 'HASH') { + require Carp; + Carp::croak("Invalid reference type '".ref(shift)."' not 'HASH'"); + } + %constants = %{+shift}; + } else { + $constants{+shift} = undef; } - my $pkg = caller; - - # Normal constant name - if ($name =~ /^(?:[A-Z]\w|_[A-Z])\w*\z/ and !$forbidden{$name}) { - # Everything is okay - - # Name forced into main, but we're not in main. Fatal. - } elsif ($forced_into_main{$name} and $pkg ne 'main') { - require Carp; - Carp::croak("Constant name '$name' is forced into main::"); - - # Starts with double underscore. Fatal. - } elsif ($name =~ /^__/) { - require Carp; - Carp::croak("Constant name '$name' begins with '__'"); - - # Maybe the name is tolerable - } elsif ($name =~ /^[A-Za-z_]\w*\z/) { - # Then we'll warn only if you've asked for warnings - if ($^W) { + + foreach my $name ( keys %constants ) { + unless (defined $name) { + require Carp; + Carp::croak("Can't use undef as constant name"); + } + my $pkg = caller; + + # Normal constant name + if ($name =~ /^_?[^\W_0-9]\w*\z/ and !$forbidden{$name}) { + # Everything is okay + + # Name forced into main, but we're not in main. Fatal. + } elsif ($forced_into_main{$name} and $pkg ne 'main') { require Carp; - if ($keywords{$name}) { - Carp::carp("Constant name '$name' is a Perl keyword"); - } elsif ($forced_into_main{$name}) { - Carp::carp("Constant name '$name' is " . - "forced into package main::"); - } elsif (1 == length $name) { - Carp::carp("Constant name '$name' is too short"); - } elsif ($name =~ /^_?[a-z\d]/) { - Carp::carp("Constant name '$name' should " . - "have an initial capital letter"); + Carp::croak("Constant name '$name' is forced into main::"); + + # Starts with double underscore. Fatal. + } elsif ($name =~ /^__/) { + require Carp; + Carp::croak("Constant name '$name' begins with '__'"); + + # Maybe the name is tolerable + } elsif ($name =~ /^[A-Za-z_]\w*\z/) { + # Then we'll warn only if you've asked for warnings + if (warnings::enabled()) { + if ($keywords{$name}) { + warnings::warn("Constant name '$name' is a Perl keyword"); + } elsif ($forced_into_main{$name}) { + warnings::warn("Constant name '$name' is " . + "forced into package main::"); + } + } + + # Looks like a boolean + # use constant FRED == fred; + } elsif ($name =~ /^[01]?\z/) { + require Carp; + if (@_) { + Carp::croak("Constant name '$name' is invalid"); } else { - # Catch-all - what did I miss? If you get this error, - # please let me know what your constant's name was. - # Write to . Thanks! - Carp::carp("Constant name '$name' has unknown problems"); + Carp::croak("Constant name looks like boolean value"); } - } - # Looks like a boolean - # use constant FRED == fred; - } elsif ($name =~ /^[01]?\z/) { - require Carp; - if (@_) { - Carp::croak("Constant name '$name' is invalid"); } else { - Carp::croak("Constant name looks like boolean value"); + # Must have bad characters + require Carp; + Carp::croak("Constant name '$name' has invalid characters"); } - } else { - # Must have bad characters - require Carp; - Carp::croak("Constant name '$name' has invalid characters"); - } - - { - no strict 'refs'; - my $full_name = "${pkg}::$name"; - $declared{$full_name}++; - if (@_ == 1) { - my $scalar = $_[0]; - *$full_name = sub () { $scalar }; - } elsif (@_) { - my @list = @_; - *$full_name = sub () { @list }; - } else { - *$full_name = sub () { }; + { + no strict 'refs'; + my $full_name = "${pkg}::$name"; + $declared{$full_name}++; + if ($multiple) { + my $scalar = $constants{$name}; + *$full_name = sub () { $scalar }; + } else { + if (@_ == 1) { + my $scalar = $_[0]; + *$full_name = sub () { $scalar }; + } elsif (@_) { + my @list = @_; + *$full_name = sub () { @list }; + } else { + *$full_name = sub () { }; + } + } } } - } 1; @@ -114,34 +122,32 @@ constant - Perl pragma to declare constants =head1 SYNOPSIS - use constant BUFFER_SIZE => 4096; - use constant ONE_YEAR => 365.2425 * 24 * 60 * 60; - use constant PI => 4 * atan2 1, 1; - use constant DEBUGGING => 0; - use constant ORACLE => 'oracle@cs.indiana.edu'; - use constant USERNAME => scalar getpwuid($<); - use constant USERINFO => getpwuid($<); + use constant PI => 4 * atan2(1, 1); + use constant DEBUG => 0; - sub deg2rad { PI * $_[0] / 180 } + print "Pi equals ", PI, "...\n" if DEBUG; - print "This line does nothing" unless DEBUGGING; + use constant { + SEC => 0, + MIN => 1, + HOUR => 2, + MDAY => 3, + MON => 4, + YEAR => 5, + WDAY => 6, + YDAY => 7, + ISDST => 8, + }; - # references can be constants - use constant CHASH => { foo => 42 }; - use constant CARRAY => [ 1,2,3,4 ]; - use constant CPSEUDOHASH => [ { foo => 1}, 42 ]; - use constant CCODE => sub { "bite $_[0]\n" }; + use constant WEEKDAYS => qw( + Sunday Monday Tuesday Wednesday Thursday Friday Saturday + ); - print CHASH->{foo}; - print CARRAY->[$i]; - print CPSEUDOHASH->{foo}; - print CCODE->("me"); - print CHASH->[10]; # compile-time error + print "Today is ", (WEEKDAYS)[ (localtime)[WDAY] ], ".\n"; =head1 DESCRIPTION -This will declare a symbol to be a constant with the given scalar -or list value. +This will declare a symbol to be a constant with the given value. When you declare a constant such as C using the method shown above, each machine your script runs upon can have as many digits @@ -150,21 +156,43 @@ read, more likely to be maintained (and maintained correctly), and far less likely to send a space probe to the wrong planet because nobody noticed the one equation in which you wrote C<3.14195>. +When a constant is used in an expression, perl replaces it with its +value at compile time, and may then optimize the expression further. +In particular, any code in an C block will be optimized +away if the constant is false. + =head1 NOTES -The value or values are evaluated in a list context. You may override -this with C as shown above. +As with all C directives, defining a constant happens at +compile time. Thus, it's probably not correct to put a constant +declaration inside of a conditional statement (like C). + +Constants defined using this module cannot be interpolated into +strings like variables. However, concatenation works just fine: -These constants do not directly interpolate into double-quotish -strings, although you may do so indirectly. (See L for -details about how this works.) + print "Pi equals PI...\n"; # WRONG: does not expand "PI" + print "Pi equals ".PI."...\n"; # right - print "The value of PI is @{[ PI ]}.\n"; +Even though a reference may be declared as a constant, the reference may +point to data which may be changed, as this code shows. -List constants are returned as lists, not as arrays. + use constant ARRAY => [ 1,2,3,4 ]; + print ARRAY->[1]; + ARRAY->[1] = " be changed"; + print ARRAY->[1]; - $homedir = USERINFO[7]; # WRONG - $homedir = (USERINFO)[7]; # Right +Dereferencing constant references incorrectly (such as using an array +subscript on a constant hash reference, or vice versa) will be trapped at +compile time. + +Constants belong to the package they are defined in. To refer to a +constant defined in another package, specify the full package name, as +in C. Constants may be exported by modules, +and may also be called as either class or instance methods, that is, +as C<< Some::Package->CONSTANT >> or as C<< $obj->CONSTANT >> where +C<$obj> is an instance of C. Subclasses may define +their own constants to override those in their base class. The use of all caps for constant names is merely a convention, although it is recommended in order to make constants stand out @@ -174,59 +202,75 @@ underscore. Names beginning with a double underscore are reserved. Some poor choices for names will generate warnings, if warnings are enabled at compile time. -Constant symbols are package scoped (rather than block scoped, as -C is). That is, you can refer to a constant from package -Other as C. +=head2 List constants -As with all C directives, defining a constant happens at -compile time. Thus, it's probably not correct to put a constant -declaration inside of a conditional statement (like C). +Constants may be lists of more (or less) than one value. A constant +with no values evaluates to C in scalar context. Note that +constants with more than one value do I return their last value in +scalar context as one might expect. They currently return the number +of values, but B. Do not use constants +with multiple values in scalar context. -Omitting the value for a symbol gives it the value of C in -a scalar context or the empty list, C<()>, in a list context. This -isn't so nice as it may sound, though, because in this case you -must either quote the symbol name, or use a big arrow, (C<=E>), -with nothing to point to. It is probably best to declare these -explicitly. +B This implies that the expression defining the value of a +constant is evaluated in list context. This may produce surprises: - use constant UNICORNS => (); - use constant LOGFILE => undef; + use constant TIMESTAMP => localtime; # WRONG! + use constant TIMESTAMP => scalar localtime; # right -The result from evaluating a list constant in a scalar context is -not documented, and is B guaranteed to be any particular value -in the future. In particular, you should not rely upon it being -the number of elements in the list, especially since it is not -B that value in the current implementation. +The first line above defines C as a 9-element list, as +returned by localtime() in list context. To set it to the string +returned by localtime() in scalar context, an explicit C +keyword is required. -Magical values, tied values, and references can be made into -constants at compile time, allowing for way cool stuff like this. -(These error numbers aren't totally portable, alas.) +List constants are lists, not arrays. To index or slice them, they +must be placed in parentheses. - use constant E2BIG => ($! = 7); - print E2BIG, "\n"; # something like "Arg list too long" - print 0+E2BIG, "\n"; # "7" + my @workdays = WEEKDAYS[1 .. 5]; # WRONG! + my @workdays = (WEEKDAYS)[1 .. 5]; # right -Dereferencing constant references incorrectly (such as using an array -subscript on a constant hash reference, or vice versa) will be trapped at -compile time. +=head2 Defining multiple constants at once -In the rare case in which you need to discover at run time whether a -particular constant has been declared via this module, you may use -this function to examine the hash C<%constant::declared>. If the given -constant name does not include a package name, the current package is -used. +Instead of writing multiple C statements, you may define +multiple constants in a single statement by giving, instead of the +constant name, a reference to a hash where the keys are the names of +the constants to be defined. Obviously, all constants defined using +this method must have a single value. - sub declared ($) { - use constant 1.01; # don't omit this! - my $name = shift; - $name =~ s/^::/main::/; - my $pkg = caller; - my $full_name = $name =~ /::/ ? $name : "${pkg}::$name"; - $constant::declared{$full_name}; - } + use constant { + FOO => "A single value", + BAR => "This", "won't", "work!", # Error! + }; + +This is a fundamental limitation of the way hashes are constructed in +Perl. The error messages produced when this happens will often be +quite cryptic -- in the worst case there may be none at all, and +you'll only later find that something is broken. + +When defining multiple constants, you cannot use the values of other +constants defined in the same declaration. This is because the +calling package doesn't know about any constant within that group +until I the C statement is finished. + + use constant { + BITMASK => 0xAFBAEBA8, + NEGMASK => ~BITMASK, # Error! + }; + +=head2 Magic constants + +Magical values and references can be made into constants at compile +time, allowing for way cool stuff like this. (These error numbers +aren't totally portable, alas.) -=head1 TECHNICAL NOTE + use constant E2BIG => ($! = 7); + print E2BIG, "\n"; # something like "Arg list too long" + print 0+E2BIG, "\n"; # "7" + +You can't produce a tied constant by giving a tied scalar as the +value. References to tied variables, however, can be used as +constants without any problems. + +=head1 TECHNICAL NOTES In the current implementation, scalar constants are actually inlinable subroutines. As of version 5.004 of Perl, the appropriate @@ -235,26 +279,33 @@ calls, thereby saving the overhead of a subroutine call. See L for details about how and when this happens. +In the rare case in which you need to discover at run time whether a +particular constant has been declared via this module, you may use +this function to examine the hash C<%constant::declared>. If the given +constant name does not include a package name, the current package is +used. + + sub declared ($) { + use constant 1.01; # don't omit this! + my $name = shift; + $name =~ s/^::/main::/; + my $pkg = caller; + my $full_name = $name =~ /::/ ? $name : "${pkg}::$name"; + $constant::declared{$full_name}; + } + =head1 BUGS In the current version of Perl, list constants are not inlined and some symbols may be redefined without generating a warning. -It is not possible to have a subroutine or keyword with the same +It is not possible to have a subroutine or a keyword with the same name as a constant in the same package. This is probably a Good Thing. A constant with a name in the list C is not allowed anywhere but in package C, for technical reasons. -Even though a reference may be declared as a constant, the reference may -point to data which may be changed, as this code shows. - - use constant CARRAY => [ 1,2,3,4 ]; - print CARRAY->[1]; - CARRAY->[1] = " be changed"; - print CARRAY->[1]; - Unlike constants in some languages, these cannot be overridden on the command line or via environment variables. @@ -263,16 +314,22 @@ automatically quotes barewords (as is true for any subroutine call). For example, you can't say C<$hash{CONSTANT}> because C will be interpreted as a string. Use C<$hash{CONSTANT()}> or C<$hash{+CONSTANT}> to prevent the bareword quoting mechanism from -kicking in. Similarly, since the C<=E> operator quotes a bareword -immediately to its left, you have to say C 'value'> +kicking in. Similarly, since the C<< => >> operator quotes a bareword +immediately to its left, you have to say C<< CONSTANT() => 'value' >> (or simply use a comma in place of the big arrow) instead of -C 'value'>. +C<< CONSTANT => 'value' >>. =head1 AUTHOR Tom Phoenix, EFE, with help from many other folks. +Multiple constant declarations at once added by Casey West, +EFE. + +Documentation mostly rewritten by Ilmari Karonen, +EFE. + =head1 COPYRIGHT Copyright (C) 1997, 1999 Tom Phoenix