in reply to is_constant() - a routine that detects constant subs

Nice bit of code. I thought I would play with it to allow for a couple of different cases. First, if someone didn't fully qualify the name, I used caller to determine the calling package and prepend that to the name. Second, what if someone passes you a bogus sub name? Then, the CODE slot in the typeglob should be undef and you get "use of unitialized value" warnings. Here's what I used to fix it and get a segfault.

package One; use strict; use warnings; use constant FOO => 1; sub bar { 1 }; foreach ( qw/ FOO One::FOO Two::FOO bar / ) { if ( Two::is_constant( $_ ) ) { print "Yes\n"; } else { print "No\n"; } } package Two; =pod is_constant($sub_name) - returns true if subroutine is a constant, false if not. $sub_name must be the fully qualified name (Package::name) of a subroutine. =cut sub is_constant { no strict 'refs'; my $name = shift; if ( $name !~ /::/ ) { $name = (caller)[0]."::$name"; } my $code = *{$name}{CODE}; # must have any empty prototype to be a constant my $proto; #print "->$code<-\n"; if ( ref $code ) { $proto = prototype($code); } return 0 if defined $proto and length $proto; # attempt to redefine to itself - this will cause a # warning for a real constant that starts with "Constant" my $is_const; { local $SIG{__WARN__} = sub { $is_const = 1 if $_[0] =~ /^Const +ant/ }; eval { *{$name} = sub () { "TEST" } }; } # set it back { no warnings; eval { *{$name} = $code; }; } # all done return $is_const; }

If you look closely, you'll see that print "->$code<-\n"; has been commented out. If it's commented out, I get a segfault. If I uncomment it, merely printing the value allows the program to continue. Anyone see anything that I am missing? I get the problem running Win98 (5.6.0) and Cygwin under Win98 (5.6.1).

Cheers,
Ovid

Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Replies are listed 'Best First'.
Re: (Segfault) Re: is_constant() - a routine that detects constant subs
by particle (Vicar) on May 19, 2002 at 20:15 UTC
    there are a few other ways of setting constants i've included. in fact, i *NEVER* use constant, i don't like the syntax. i don't think => looks like assignment (i know, use constant makes it pretty obvious what it is, but i still use sub foo(){} anyway.)

    see what this gives you...

    sub bar { 1 }; sub baz() { 1 }; *zip = sub{ 1 }; *zap = sub(){ 1 }; foreach ( qw/ FOO One::FOO Two::FOO bar baz zip zap / ) { if ( Two::is_constant( $_ ) ) { print "$_ Yes\n"; #$_ so i know which one i'm checking } else { print "$_ No\n"; } }
    samtregar, i'm glad you got this code working. it stuck in my head yesterday when i saw it mentioned, but i was too involved in the release of star wars to do anything about it.

    ~Particle *accelerates*

      I don't think that "bar" and "zip" are constants. As far as I know constants must have a prototype of "()". My is_constant() uses that to avoid testing subs that can't possibly be constants, but maybe that logic is flawed and I need to test all subs?

      -sam

        i'm sorry, perhaps i wasn't clear. i was using bar/baz and zip/zap as non-constant/constant pairs. you are correct in that an empty prototype is a necessary condition for a subroutine to be optimized to a constant.

        and truly, thanks for this code. i've been working on a module (possibly named Devel::TrackSub,) which will wrap all subs in any namespaces you specify. it'll be posted as soon as i finish the pod--perhaps tomorrow.

        developing this code was my first attempt at walking the symbol table, and at first seemed a little over my head. i think i've found an elegant solution that can be quite instrumental to others. i'm excited to post it soon.

        i think you've done the same here.

        ~Particle *accelerates*

        As far as I know constants must have a prototype of "()".

        Right. So you can also see if no prototype is set. You can do return 0 unless defined $proto and not length $proto; to cover all alternatives. As it is now you test subroutines with no prototypes. Also, I'd prefer if $is_const was initialized to 0 since the return statements returns dito.

        Cheers,
        -Anomo
Re: (Segfault) Re: is_constant() - a routine that detects constant subs
by samtregar (Abbot) on May 19, 2002 at 20:14 UTC
    Yup, I get the segfault too - Perl 5.6.1 on Linux. Very strange. I'm compiling bleadperl now; maybe it's already been fixed?

    -sam

      Argh. Not only is it not fixed in bleadperl, but I think bleadperl also has a bug. Check out the output using bleadperl:

      $ ./perl ~/crash.pl Constant subroutine One::FOO redefined at /home/sam/crash.pl line 59. Yes Constant subroutine One::FOO redefined at /home/sam/crash.pl line 59. Yes Segmentation fault (core dumped)

      So, not only is it segfaulting, but it's also printing warnings even when "no warnings" is in effect! Argh!

      -sam

        According to Rafael Garcia-Suarez this is actually a deliberate change. Overwriting a constant sub is now a mandatory warning in 5.7.3. Ho hum. So now I'll just catch both errors with $SIG{__WARN__} instead of the first.

        -sam