Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Conditional Compiling

by ikegami (Patriarch)
on Nov 17, 2004 at 21:06 UTC ( [id://408586]=perlquestion: print w/replies, xml ) Need Help??

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

People who have read the Apache's mod_rewrite documentation may remember this comment:
To disable the logging of rewriting actions it is not recommended to set Filename to /dev/null, because although the rewriting engine does not then output to a logfile it still creates the logfile output internally. This will slow down the server with no advantage to the administrator! To disable logging either remove or comment out the RewriteLog directive or use RewriteLogLevel 0!
For fun, I decided the write a Perl script whose parse tree is devoid of all logging code, unless logging is enabled by a command line argument.

script.pl

use strict; use warnings; use do_compile_time_arg_check; # Checks if the first arg is --log or --log="file name". # Imports LOGGING as a constant sub. # Imports strict-safe $LOG_FILE_NAME. if (LOGGING) { if (defined($LOG_FILE_NAME)) { open(LOG_FH, '>>', $LOG_FILE_NAME) or die("Can't open log file: $!\n"); } else { open(LOG_FH, '>&STDERR') or die("Can't dup STDERR: $!\n"); } } print LOG_FH ('Opening log file at '.localtime().".$/") if LOGGING;

do_compile_time_arg_check.pm

# The first time [[ use do_compile_time_arg_check; ]] # or [[ use do_compile_time_arg_check (); ]] is used, # the first command line arg is removed from @ARGV if # it's [[ --log ]] or [[ --log="file name" ]]. # Whenever [[ use do_compile_time_arg_check; ]] is used # (including the first time), # # - [[ LOGGING ]] is exported as a constant sub. It returns # true if [[ --log ]] or [[ --log="file name" ]] was found # at the head of the argument list the first time this # module was used. # # - [[ $LOG_FILE_NAME ]] is exported as a strict-safe global. # It contains the file name from [[ --log="file name" ]] if # that argument was found at the head of the argument list # the first time this module was used. use strict; use warnings; package do_compile_time_arg_check; use vars qw( $log $log_file ); BEGIN { if (@ARGV && $ARGV[0] =~ /^-?-log(?:=(.*))?$/) { shift(@ARGV); $log = 1; $log_file = $1; } } sub import { my $caller_pkg = caller(); my $lexical_log = $log; { no strict 'refs'; *{"${caller_pkg}::LOGGING" } = sub () { $lexical_log }; *{"${caller_pkg}::LOG_FILE_NAME"} = \$log_file; } } 1;

How well does it work?

Parse tree without logging:

>perl -MO=Terse script.pl Useless use of a constant in void context at script.pl line 10. Useless use of a constant in void context at script.pl line 20. LISTOP (0x1df2e3c) leave [1] OP (0x1dfb290) enter COP (0x1df2e88) nextstate OP (0x1ba34e4) null [5] COP (0x1dfb2d4) nextstate OP (0x1dfb174) null [5] script.pl syntax OK

Parse tree with logging:

>perl -MO=Terse script.pl --log LISTOP (0x1df2644) leave [1] OP (0x1df23b0) enter COP (0x1df266c) nextstate LISTOP (0x1df26c8) leave OP (0x1df26a8) enter COP (0x1df26f0) nextstate UNOP (0x1df272c) null LOGOP (0x1df2750) cond_expr UNOP (0x1df3e78) defined UNOP (0x1dfb770) null [15] PADOP (0x1ba2fd4) gvsv 1 LISTOP (0x1dfb1e4) leave OP (0x1df2778) enter COP (0x1dfb20c) nextstate UNOP (0x1dfb248) null LOGOP (0x1dfb26c) or LISTOP (0x1dfb6f0) open [4] OP (0x1dfb6d0) null [3] PADOP (0x1dfb534) gv 3 SVOP (0x1dfb718) const SPECIAL #0 Nul +lsv UNOP (0x1dfb670) null [15] PADOP (0x1dfb694) gvsv 2 LISTOP (0x1dfb424) die [9] OP (0x1dfb294) pushmark UNOP (0x1dfb478) null [67] OP (0x1dfb458) null [3] BINOP (0x1dfb4a0) concat [7] BINOP (0x1dfb2b4) concat [6] SVOP (0x1dfb400) const SP +ECIAL #0 Nullsv UNOP (0x1dfb300) null [15] PADOP (0x1dfb3dc) gvsv + 5 SVOP (0x1dfb2dc) const SPECIA +L #0 Nullsv LISTOP (0x1df27b8) leave OP (0x1df2798) enter COP (0x1df27e0) nextstate UNOP (0x1df281c) null LOGOP (0x1df2840) or LISTOP (0x1df2a6c) open [11] OP (0x1dfb164) null [3] PADOP (0x1df2a48) gv 10 SVOP (0x1dfb188) const SPECIAL #0 Nul +lsv LISTOP (0x1df2970) die [16] OP (0x1df2868) pushmark UNOP (0x1df29c0) null [67] OP (0x1df29a0) null [3] BINOP (0x1df29e8) concat [14] BINOP (0x1df2888) concat [13] SVOP (0x1dfb1c0) const SP +ECIAL #0 Nullsv UNOP (0x1df2928) null [15] PADOP (0x1df294c) gvsv + 12 SVOP (0x1dfb4fc) const SPECIA +L #0 Nullsv COP (0x1df23d0) nextstate LISTOP (0x1df2570) print OP (0x1df2550) pushmark UNOP (0x1df240c) rv2gv PADOP (0x1df2620) gv 21 SVOP (0x1df25dc) const SPECIAL #0 Nullsv UNOP (0x1df2598) scalar OP (0x1df25bc) localtime [17] UNOP (0x1df24b8) null [67] OP (0x1df2498) null [3] BINOP (0x1df24e0) concat [19] SVOP (0x1df2508) const SPECIAL #0 Nullsv UNOP (0x1df2430) null [15] PADOP (0x1df2454) gvsv 18 script.pl syntax OK

Not bad!

Is there any way to simply avoid the "Useless use of a constant in void context"? I don't get them if I type in sub LOGGING () { 0 } instead of importing LOGGING. Update: Replacing sub LOGGING () { undef } with sub LOGGING () { 0 } removes the warnings.

Is there a module that already does something similiar?

Replies are listed 'Best First'.
Re: Conditional Compiling
by Arunbear (Prior) on Nov 17, 2004 at 21:30 UTC
    Since LOGGING is a flag rather than a true constant, why not treat it the same way as you have treated $LOG_FILE_NAME i.e. as a (read only) scalar:
    *{"${caller_pkg}::LOGGING"} = \$lexical_log;
      Because log(...) if LOGGING compiles to nothing given sub LOGGING () { 0 } (refer to the first parse tree in the OP), whereas, log(...) if $LOGGING would cause $LOGGING to be evaluated at runtime everytime the line is encountered. While that's probably a negligable cost, it doesn't meet the goal I set at the top of my post. That goal can be alternatively stated as "I want the if to execute at compile time instead of at run-time (while keeping the code simple)."
        Ok, I get it now. It's quite a cool technique which is also used by Carp::Assert (for disabling assertions).
Re: Conditional Compiling
by Fletch (Bishop) on Nov 17, 2004 at 21:22 UTC

    POE has a preprocessor which is used to do something similar. And you might get around the warning by using LOGGING() instead.

      LOGGING() doesn't change anything.

      &LOGGING gets rid of the warning ...and of the LOGGING's status as a constant.

      I'll be checking how use constant does it shortly

      Update: Setting $log to 0 instead of undef for false removes the warnings.

Re: Conditional Compiling
by data64 (Chaplain) on Nov 18, 2004 at 10:30 UTC

    Is there a module that already does something similiar?

    I use Log::Log4perl to implement something quite similar.

Re: Conditional Compiling
by ikegami (Patriarch) on Nov 19, 2004 at 18:16 UTC

    For some reason, I thought I had to use a seperate module for the sub () {} optimizations to occur, but the following works fine.

    use strict; use warnings; use vars qw( $LOG_FILE_NAME ); BEGIN { if (@ARGV && $ARGV[0] =~ /^-?-log(?:=(.*))?$/) { *LOGGING = sub () { 1 }; $LOG_FILE_NAME = $1; } else { *LOGGING = sub () { 0 }; } } if (LOGGING) { if (defined($LOG_FILE_NAME)) { open(LOG_FH, '>>', $LOG_FILE_NAME) or die("Can't open log file: $!\n"); } else { open(LOG_FH, '>&STDERR') or die("Can't dup STDERR: $!\n"); } } print LOG_FH ('Opening log file at '.localtime().".$/") if LOGGING;

    perl -MO=Terse script.pl

    LISTOP (0x1df5678) leave [1] OP (0x1df55fc) enter COP (0x1df56c0) nextstate OP (0x1ba3544) null [5] COP (0x1df563c) nextstate OP (0x1df53f8) null [5] script.pl syntax OK

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://408586]
Approved by kvale
Front-paged by Courage
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2024-03-30 08:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found