I often find it handy to watch how a variable is changed by tieing it and monitoring what tie methods are called. Here's an easy way to do it.
use strict; use warnings; { package Debug::Tie::StdScalar; use Tie::Scalar; use Carp; sub AUTOLOAD { our $AUTOLOAD =~ s/Debug:://; carp join " >|< ", "calling $AUTOLOAD", @_[1..$#_], ""; goto &$AUTOLOAD; } } # example use my $x; tie $x, 'Debug::Tie::StdScalar', 4; print $x; $x = 3; $x .= "abc"; ++$x;
If tracking multiple tied variables, try something like this instead:
use strict; use warnings; { package Debug::Tie::StdScalar; use Tie::Scalar; use Carp; sub TIESCALAR { my $obj = &Tie::StdScalar::TIESCALAR(@_); carp join(" >|< ", "calling Tie::StdScalar::TIESCALAR", @_[1..$# +_]), "\n=> ", 0+$obj; $obj; } sub AUTOLOAD { our $AUTOLOAD =~ s/Debug:://; carp join " >|< ", "calling $AUTOLOAD", 0+$_[0], @_[1..$#_], ""; goto &$AUTOLOAD; } }
Replace Scalar and SCALAR with hash or array to watch aggregates.

Update: An obvious enhancement is to catch and show what FETCH returns; up to now I haven't needed to do so, so it is left as an exercise for the reader.

Replies are listed 'Best First'.
Re: Debugging with tied variables
by rob_au (Abbot) on Dec 08, 2003 at 02:26 UTC
    Very nice ++ysth Another option to perform this type of debugging would be to employ Tie::Watch

     

    perl -le "print+unpack'N',pack'B32','00000000000000000000001010010111'"

      Cute. There are two Tie::Watches, one standalone and one bundled with Tk. They seem similar but have different version numbers :(. I think they could use a layer above to just do logging to a file; as is they look almost too featureful to be usable.

      Update: correct grammar.

Re: Debugging with tied variables
by davido (Cardinal) on Dec 08, 2003 at 16:58 UTC
    This discussion motivated me to dig into tied scalars a little deeper. My goal was to create a tie by which any use of a given scalar would result in a write to a logfile, for debugging purposes. This is very quick and dirty, but here's what I've come up with. I would really appreciate comments that might lead me to improving it.

    use strict; use warnings; package Trackit; my $varname; sub TIESCALAR { my $class = shift; $varname = shift; bless \my($self), $class; } sub STORE { ${ $_[0] } = $_[1]; document ( "STORE", $_[1] ); return ${ $_[0] }; } sub FETCH { my $self = shift; document( "FETCH", ${$self} ); return ${ $self } } sub DESTROY { my $self = shift; document ( "DESTROY", ${$self} ); } sub document { my ( $action, $value ) = @_; my $fh; open $fh, ">>log.txt" or die $!; print $fh "$varname => Action: $action, Value = $value\n"; close $fh; } # Begin main ---------------------------- package main; my $var; tie $var, "Trackit", '$var'; # Test our handywork... $var = 10; $var = $var + 10; open FH, "<log.txt" or die $!; while ( <FH> ) { print $_; } close FH;

    Ok, here are the areas I see a need for improvement. First, it seems kludgy to have to pass as a string the name of the variable we're working with so that it can be printed to the logfile. Next, I haven't provided a way to user-specify the name of the logfile, but that's an easy fix. ...anything else?


    Dave

      You forgot to provide a SCALAR method...oh no, wait, that's only a method for tied hashes :)

      It looks ok. I would have made it subclass Tie::StdScalar, but scalars are simple enough that it is probably more work to do that. If you do it for hashes or arrays, its more clear that subclassing is the way to go.

      For those who are unaware, the perl core has classes Tie::StdHash, Tie::StdArray, Tie::StdScalar, and Tie::StdHandle that just act like normal hashes, arrays, scalars, and filehandles (to the extent possible through the tie interface). For less than obvious reasons, these classes are in files named without the "Std", so, for example, to tie with Tie::StdScalar, you need to say use Tie::Scalar.

      Update: since you are storing the variable name, you would need to subclass the Tie::Extra* versions that allow extra storage, and so far only Tie::ExtraHash has actually been written :(.

      Update: you might want to log caller() information, too.