hi, In continuation with my old node :

removing debugging code at compile time ?

Today again :) i tried to figure a way to lower the number of the typed characters for doing Debugging, Logging and eventually error reporting.

So the question was how can i plug some debugging info into modules :
- which I can control from outside
- Is removed from the code if disabled i.e. in production I don't want this to take cpu cycles (pruned at compile time)
- is short to write i.e. as less keystrokes as possible.

To solve the dillema, my thought was that I have to use constants. The other tweak I decided to use sub(){} <=> constants, so I can add code
The problem was that to be able to mimic sub() like constant and vs. versa the prototype of the sub has to be sub(){...}
So far so good, but on the other hand I wanted to pass as many parameters as I want to this sub w/o getting the "Prototype mismatch error".
The solution was to call the sub like this &mysub($p1,$p2,..), so that it does not check the prototype
And here is my final solution Slicer module

package Slicer; use base Exporter; use Data::Dumper; our @EXPORT = qw(slice unslice); sub slice() { my @caller = caller; print $caller[0]; print ":: sliced :" , @_; } sub unslice { for my $m (@_) { eval "sub $m\::slice() {}"; } } 1;
Think of slice() as anything you write i.e. debug, error,log, whatever ... (this one is just example for experiments)
Then I have my test module SomeModule.pm

package SomeModule; use Slicer; sub something { #code.... &slice("let me say something\n"); } 1;
and finally the test script to see if all this works
#!/usr/bin/perl use Slicer; #unslice 'SomeModule'; use strict; use SomeModule; #use constant slice => 0; #sub slice() {} ; sub mainsub { my $x = 12;#more code.... &slice("mainsub\n"); } slice && print "&& sliced\n"; print "if sliced\n" if slice; &slice(debug => "main\n"); mainsub; SomeModule::something; slice;
As you can see I can use all different methods to trigger slicing:
if, &&, slice, &slice(params)
Then I can disable slicing in many ways too i.e. :
use constant, sub() {}, unslice(@ModulesNames)

(Slicing in a sense that i slice the code at this point, then in my slice function I will implement all debug,log,error logic. It could be 'fat' 'cause it is pruned when I disable it anyway. Or if needed I can implement separate sub/constants logto,debug,err and slice :) ).

The things that still bothers me :
- I dont like &slice(...) syntax very much. If I can will prefer "&slice param1 => 123,p2 => 223", but using & requires braces ;(
- I'm not sure does unslice(), has to be executed in BEGIN{} !! Is the code pruned if i call it this way ? Yes I see slice() is not executed, but does it really remove the call to it ?

Any more comments are welcome...

PS> Dont forget i define slice with prototype i.e. slice()

Replies are listed 'Best First'.
Re: Cheaper - Debug,Log,Errors via Slicing&dicing
by ikegami (Patriarch) on Jul 26, 2007 at 19:39 UTC

    I'm not convinced your endeavour is worthwhile, but I did find a problem.

    Assuming it works at all, unslice executes too late in

    unslice 'SomeModule'; use SomeModule;

    It needs to be

    BEGIN { unslice 'SomeModule'; } use SomeModule;

    A better syntax might be

    no slice 'SomeModule'; use SomeModule;

    Further more, your slice isn't constant. You might as well remove the prototype. Using the () prototype is not enough to make a constant. The body of the function must also be constant. From perlsub:

    Functions with a prototype of () are potential candidates for inlining. If the result after optimization and constant folding is either a constant or a lexically-scoped scalar which has no other references, then it will be used in place of function calls made without &.

      right, I wasn't sure unslice() is working as expected..
      I agree that this syntax is better :
      no slice 'SomeModule';
      Does this mean I have to implement module called 'slice' and then implement the unimport method. Is that what you meant ? I haven't done 'no'-module !

      >>Further more, your slice isn't constant.
      That's right, that was my idea. When I don't want to do debugging it is defined as constant, so it is inlined and pruned.
      But when I need to debug it is a full blown sub. i.e. if I have the following code :
      use Slicer; use constant slice => 0;
      The constant will override the Slicer.pm definition, so no code will be executed.At least this is my understanding how it works and it seems to work.

      In short to be inlined it has to be constant/sub-constant. To be able to override a constant I need a sub with prototype (), so that I can use the same name.
      And finally to be able to call such sub() with many parameters I have to call it like this &mysub($p1,$p2)
      Is my reasoning correct or there is a hole in it ;)

        Does this mean I have to implement module called 'slice' and then implement the unimport method. Is that what you meant ? I haven't done 'no'-module !

        I meant no Slicer 'SomeModule';, so you would create an unimport function instead of of unslice.

        The constant will override the Slicer.pm definition

        It will do that even if slice has no prototype. I notice you get a warning when you override, and two if there's a prototype mismatch, so may I suggest

        use if $ENV{SLICE}, 'Slicer'; use if !$ENV{SLICE}, 'constant', slice => 0;

        or better yet, move that logic into Slicer:

        package Slicer; use base Exporter; our @EXPORT = qw( slice ); sub _slice { my @caller = caller; print $caller[0]; print ":: sliced :" , @_; return 1; } if ($ENV{SLICE}) { *slice = \&_slice; } else { require constant; import constant slice => 0; } 1;

        Update: Nevermind, still doesn't work for slice(...). You really should use two functions instead of requiring people to use &.

        package Slicer; use base Exporter; our @EXPORT = qw( slice sliced ); use constant slice => $ENV{SLICE}; sub sliced { return unless slice; my @caller = caller; print $caller[0]; print ":: sliced :" , @_; } 1;
        >set SLICE= >perl -wle"use Slicer; print('!') if slice; >perl -wle"use Slicer; sliced('!'); >set SLICE=1 >perl -wle"use Slicer; print('!') if slice; ! >perl -wle"use Slicer; sliced('!'); main:: sliced :!
Re: Cheaper - Debug,Log,Errors via Slicing&dicing
by andreas1234567 (Vicar) on Jul 26, 2007 at 06:32 UTC
    1. which I can control from outside
    2. Is removed from the code if disabled i.e. in production I don't want this to take cpu cycles (pruned at compile time)
    3. is short to write i.e. as less keystrokes as possible.

    Perhaps the Log::Log4perl module could be useful? It satisfies the first and third of your requirements, but not the second entirely and the second in the sense that you can leave debug statements in production code and turn it on/off as you please. There is a small performance penalty, however, since at least one if test is performed for each statement. There is a performance discussion in the FAQ.

    Update: Clarified the second requirement.

    --
    Andreas