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

hi,
I have something like this :
sub say { print "$_\n" for @_; } sub debug { my ($msg,$lvl) = @_; say $msg if $lvl && $DEBUG{$lvl} } sub debug_lvl { map $DEBUG{$_} = 1, @_ }
then in my code I have alot of :
debug "this...", LVL1; ... debug "that ..", LVL3;
The question is ? Is there a way to prune the debug() call at all when I've not activated debug mode.
I looked at Assert::Carp, I need to add "if DEBUG".
And assertions.pm work above 5.9.
assert::compat, from what I understand will not remove the code.
So is there a way in perl5 to remove a sub() call easy w/o adding additional checks ?
Second question : If I use it like this :
DEBUG and debug "text..", LVL1 ;
will this remove the code when DEBUG constant is not set ?

Replies are listed 'Best First'.
Re: removing debugging code at compile time ?
by Joost (Canon) on Jun 01, 2007 at 21:58 UTC
    If DEBUG is a defined, in scope constant like the ones created by constant (i.e. a sub containing a constant expression with a () prototype), then, DEBUG and .... will probably prune the code if DEBUG is false due to code folding.

    As far as I know, that's the best you can do with current production-ready perls, unless you want to go into the mess that's source filters.

    update: if you have the stomach, take a look at the -P option in perlrun.

      from the docs you pointed it seems that even if I use this :
      debug "text", LVL1 if DEBUG;
      instead of this :
      DEBUG and debug "text", LVL1;
      it will still prune the debug() call at compile time (if DEBUG is 0, of course), right ?
Re: removing debugging code at compile time ?
by grinder (Bishop) on Jun 02, 2007 at 06:12 UTC

    sidenote: avoid getting into the habit of defining your own say. Either use a Perl 5 module that defines it, wait until 5.10 or Perl 6, or give it another name.

    Much less potential for strife that way.

    • another intruder with the mooring in the heart of the Perl

Re: removing debugging code at compile time ?
by tinita (Parson) on Jun 02, 2007 at 16:30 UTC
    i'm using something like that:

    use constant DEBUG => $ENV{DEBUG_ME} || 0;

    that way i can switch debugging on just by setting an environment variable.

Re: removing debugging code at compile time ?
by Jenda (Abbot) on Jun 02, 2007 at 22:09 UTC

    The way you have the code I think the easiest solution would be to change the meaning of the LVLx constants from "the ID of the level" (where most likely LVL1 == 1, LVL2 == 2, etc.) to "is the debug for this level turned on?". So that you could change your debug statements to

    debug "this..." if LVL1; ... debug "that .." if LVL3;
    You'd have to change the debug() subroutine to just
    sub debug { print "$_\n" for @_; }
    and make sure the LVLx constants are defined in time ... either by enclosing the debug level setting and constant definition in a BEGIN{} block or by doing that in a module you use on top of the program. Something like
    BEGIN { ... read the levels that should be ON to %debug for (1.. $count_of_levels) { eval "sub LVL$_ () {$debug{$_}};"; } }

      cool...now how to push this BEGIN{} block from the MyUtil.pm to the package which is using it, so I dont repeat the same thing in every package
      Otherwise I have to do :
      debug 'this' if MyUtils::LVL1;
      which is ugly ;). Is this OK.
      ... eval "sub main::LVL$_ () {$debug{$_}}" ...

        If you define the constants in MyUtils.pm and only use them outside you do not need to do anything special. Define them any way you need and include them in the @EXPORT array. Something like

        package MyUtils; use base Exporter; our @EXPORT = qw(LVL1 LVL2 LVL3 debug); sub debug { print "$_\n" for @_; } my $count_of_levels = 3; our %debug = ( # fake initialization 1 => 1, 2 => 1, 3 => 0 ); for (1 .. $count_of_levels) { eval "sub LVL$_ () {$debug{$_}};"; } 1;
        and
        use strict; use MyUtils; print "Starting\n"; debug "blah 1" if LVL1; debug "blah 2" if LVL2; debug "blah 3" if LVL3; print "Ende\n";
        The reason you do not need to use a BEGIN block this way is that the code in MyUtils.pm outside any subroutines is evaluated before perl returns to the file containing the use statement and continues parsing it. So the LVLx constants do get defined in time.

Re: removing debugging code at compile time ?
by aufflick (Deacon) on Jun 02, 2007 at 13:12 UTC
    You could look into Filter::Simple, but you'd need to be very careful.

    And to save everyone the bother, many people say you shouldn't ever use source filtering in production code.

    I think that's like saying you should never use goto. Of course you should *rarely* use goto and if you don't understand why goto is considered so harmful then you shouldn't use it until you do. I think source filtering is the same. (cf the recent discussion Re: Breakin' the rules, Breakin' the rules)

how do you check if it's compiled away?
by Anonymous Monk on Aug 17, 2007 at 03:59 UTC
    here's something i just came up with. if you have the module My::Module, it will check for MY_MODULE_DEBUG.
    use constant DEBUG => do { (my $p = __PACKAGE__ ) =~ tr/a-z:/A-Z_/s; $ENV{ $p . '_DEBUG' } || 0; };

      with the minor gotcha that 'Apple::Peeler' becomes 'APLE_PELER_DEBUG'.


      DWIM is Perl's answer to Gödel
        darn, i forgot about that. i had originally had a `uc` i was trying to optimize away. so the working version is:
        use constant DEBUG => do { (my $p = uc __PACKAGE__ ) =~ tr/:/_/s; $ENV{ $p . '_DEBUG' } || 0; };
Re: removing debugging code at compile time ?
by Anonymous Monk on Aug 17, 2007 at 07:19 UTC
    Is it possible to invoke debugging mode via import and still have it compiled away if not set? i.e. can you invoke via:
    use My::Module qw(DEBUG);