SIGSEGV has asked for the wisdom of the Perl Monks concerning the following question:

Hi Perlmonks,

I have a silly problem.

In a script I defined many constants through the constant module.
I want to implement a function that dumps all those constants when the script is invoked with a certain option.

To show you what I try to accomplish please have a look at the sample snippet below.

The only way I got access to the constants was through an eval(). But then it still fails if the constant holds a list of values.

I don't know if constants are at all part of the symbol table so my whole assumption of accessing them through symrefs may be absurd altogether.

Any suggestions?

#!/usr/bin/perl use strict; use constant { CLOUD => 9, HELL => 'sytem administration' }; use constant SINS => qw(pride envy gluttony lust anger greed sloth) +; dump_consts(); sub dump_consts { no strict 'refs'; print "SINS list:\t@{[SINS]}\n\n"; foreach my $const (keys %constant::declared) { printf "%-15s%6s => %s\n", 'eval:', $const, eval $const; printf "%-15s%6s => %s\n", 'symref:', $const, {$const}; printf "%-15s%6s => %s\n", 'symref fqn:', $const, {"constant::$con +st"}; printf "%-15s%6s => %s\n", 'symref hash:', $const, join(' ', values %{$const}); } }

This produces this output:

SINS list: pride envy gluttony lust anger greed sloth eval: main::CLOUD => 9 symref: main::CLOUD => HASH(0x4002da60) symref fqn: main::CLOUD => HASH(0x4002da60) symref hash: main::CLOUD => eval: main::HELL => sytem administration symref: main::HELL => HASH(0x4005c5f0) symref fqn: main::HELL => HASH(0x4005c5f0) symref hash: main::HELL => eval: main::SINS => pride symref: main::SINS => HASH(0x4005c5fc) symref fqn: main::SINS => HASH(0x4005c5fc) symref hash: main::SINS =>

Replies are listed 'Best First'.
Re: Accessing constants via symbol table possible?
by steves (Curate) on Feb 07, 2003 at 15:23 UTC

    It may help to know what the const pragma does. It simply creates functions (subs) that are constant. A sub prototyped with () is defined to take no arguments and is then in-lined. Here's an evil method I wrote but never used in live code that may help you dig a little further:

    sub const_override { my $name = shift; my $pkg; if ($name =~ /^(.*)::([^:]+)$/) { $pkg = $1; $name = $2; } else { $pkg = caller; } no strict 'refs'; if (!defined(*{"${pkg}::$name"})) { croak "A constant named '${pkg}::$name' has not been defined." +; } carp "Constant override for ${pkg}::$name not in BEGIN block may f +ail" if (defined($^S)); local $SIG{__WARN__} = sub { warn @_ if (join(" ", @_) !~ /Constant subroutine .* redefined +/); }; if (@_ == 1) { my $scalar = $_[0]; *{"${pkg}::$name"} = sub () { $scalar }; } elsif (@_) { my @list = @_; *{"${pkg}::$name"} = sub () { @list }; } else { my $set; *{"${pkg}::$name"} = sub () { }; } }

Re: Accessing constants via symbol table possible?
by chromatic (Archbishop) on Feb 07, 2003 at 17:26 UTC
Re: Accessing constants via symbol table possible?
by diotalevi (Canon) on Feb 07, 2003 at 16:40 UTC

    Just call the function by name. Constants are functions so... it just works. No eval necessary.

    sub dump_consts { print "SINS list: @{[SINS]}\n\n"; no strict 'refs'; local $, = ' '; for my $const (keys %constant::declared) { print $const, $const -> (), "\n"; } } __DATA__ SINS list: pride envy gluttony lust anger greed sloth main::CLOUD 9 main::SINS pride envy gluttony lust anger greed sloth main::HELL system admin

    Seeking Green geeks in Minnesota

      Hi dio,

      I have to apologize for this long delay until giving feedback, but I only come round here so seldomly.

      Your hint was absolutely right. I should have had a look at the source of constant.pm (by invoking perldoc with the -m switch) to realize that the constants are only inlined subs.

      Regards
Re: Accessing constants via symbol table possible?
by PodMaster (Abbot) on Feb 07, 2003 at 16:31 UTC
    Learn to love the Devel:: namespace, I do ;)
    use Devel::Symdump; use strict; use warnings; use constant SINS => qw(pride envy gluttony lust anger greed sloth) +; my $dS = Devel::Symdump->new(__PACKAGE__); local $\="\n"; print " here are some constants (prototype '')"; for my $f( $dS->functions ) { my $p = prototype $f; print $f if defined $p and $p eq ''; } __END__ here are some constants (prototype '') main::SINS


    MJD says you can't just make shit up and expect the computer to know what you mean, retardo!
    ** The Third rule of perl club is a statement of fact: pod is sexy.

      Unfortunately your snippet will report subroutines that aren't constants as constants. Try adding
      my $foo = 1; sub foo () { $foo++ }
      ihb
        I know (that can only be determined through constant.pm like you know). You should've mentioned the fact that I completely missed the question.

        update: you see me saying you in italycs like that, imagine i'm nodding at you as I say it, mmkay.

        Also, chromatic also likes the Devel:: namespace ;)


        MJD says you can't just make shit up and expect the computer to know what you mean, retardo!
        ** The Third rule of perl club is a statement of fact: pod is sexy.

Re: Accessing constants via symbol table possible?
by ihb (Deacon) on Feb 07, 2003 at 16:42 UTC
    Here's a little snippet to dump the constants declared by constant.pm in the current package:
    sub dump_consts_in_current_package { no strict 'refs'; printf "%s => %s\n", $_, join ', ', $_->() foreach grep $_ =~ ('^' . __PACKAGE__ . '::'), keys %constant::d +eclared; }

    A brief explanation of the code. Constants are special subroutines, check out perlsub, but can still be called as any other subroutine. I choose to treat it as a symbolic reference, and dereference it immediately with the arrow notation; see perlref. To get only the current package's constants I use grep() to filter out those that begin with the current package's name. The perhaps unusual pattern is nothing but an expression that's later interpreted as a pattern.

    That's it. :)

    (As a side-note: The grep() above won't be slow because of the "special" pattern. __PACKAGE__ is compile-time so a constant will be folded in, and two constants concatenated is optimized to be just one constant. This in combination with that identical patterns at the same place won't recompile makes this not impose any overhead. You could interpolate __PACKAGE__ and use the o modifier to make the pattern compile at program compile-time. (Update: If I interpolate it won't be precompiled anyway... so the only thing gained by using the o modifier is that reinterpolation won't occure.) But the o modifier doesn't work in activeperl, at least not my activeperl, so I never use it. (Update: Clarification per request: With the o modifier I mean the "compile only once" modifier you can put on pattern quote ops, like m/PATTERN/o.)

    Hope I've helped,
    ihb

      /o doesn't make the pattern compile at perl's compile time. It just means that when the expression is used and compiled it keeps the result instead of throwing away the compiled form. It doesn't imply that the regex itself is compiled at any particular time.


      Seeking Green geeks in Minnesota

        I'm sorry, I was obviously smoking. I didn't mean that /o makes the pattern compile at program compile-time. The idea was to make perl do precompilation (at compile-time) by making it a match operator instead of an expression. (Perl precompiles constant patterns.) The /o was supposed to make sure that the pattern wasn't recompiled. But how I managed to perform this logic I don't understand myself. Because if I interpolate, precompilation can't occure anyway. And the /o is close to totally unnecessary, since the pattern will be the same and the optimization I mentioned myself about identical patterns will kick in. So the only thing saved is interpolation time.