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

I have had a lot of trouble, and since worked out the problem, however i dont know why my solution works.

The module (below) is supposed to open a logfile with a supplied name (logger->new(name=>'logname')then simply append to the logfile with a couple of strings eg: logger->log('START','Started prgy at $time').

The problem is in the log method. if i write the code:
my $fh = $self->{_handle}; print $fh "$location, $message. \n";

it all works and is cool. my question is why cant i use $self->{_handle}directly (rather than $fh) with the print statement? as always any comments on the code are welcome..

most of the style here is copied frim "Object Oriented Perl - Conway".


package Logger; $VERSION = 1.00; use strict; use vars qw( $AUTOLOAD ); use Carp; { #Encapsulated class data my %_log_attr = # DEFAULT ACCESSIBILITY ( _name => ['DEFAULT', 'read/write'], _handle => [undef, 'read'] ); #is a specirfied object attribute accessible in a given mode sub _accessible { my ($self, $attr, $mode) = @_; $_log_attr{$attr}[1] =~ /$mode/ } # Classwide default value for a specified object attributes sub _default_for { my ($self, $attr) = @_; $_log_attr{$attr}[2]; } # list of names of all specified object attributes sub _standard_keys { keys %_log_attr; } } sub new { my ($caller, %arg) = @_; my $caller_is_obj = ref($caller); my $class = $caller_is_obj || $caller; my $self = bless {}, $class; foreach my $attrname ($self->_standard_keys() ) { my ($argname) = ($attrname =~ /^_(.*)/); if (exists $arg{$argname}) { $self->{$attrname} = $arg{$argname} ; } elsif ($caller_is_obj) { $self->{$attrname} = $caller->{$argname}; } else { $self->{$attrname} = $self->_default_for($attrname) } } $self->{_handle}=&prepare_file($self); return $self; } sub prepare_file { my $self = shift; if ($self->{_name}) { my @time=localtime; ++$time[4]; if (length($time[3])eq 1 ) {$time[3]='0'.$time[3]}; if (length($time[4])eq 1 ) {$time[4]='0'.$time[4]}; $time[5]=$time[5]+1900; my $name = '>>'.$self->{_name}.".".$time[3].$time[4].$time[5]. +'.log'; local*LOGFILE; open(LOGFILE, $name) || undef; return *LOGFILE; } } sub log { use Data::Dumper; #print Dumper(@_); my ($self, $location, $message) = @_; if ($self->{_handle}) { my $fh = $self->{_handle}; print $fh "$location, $message. \n"; } else { print "Must call logger->new(name=>'<logfile>') before writing + log"; } } 1

Replies are listed 'Best First'.
Re: dereferencing file handles
by bwana147 (Pilgrim) on Sep 03, 2001 at 11:48 UTC

    The Cookbook (I haven't got it at hand, so I can't quote) says that a filehandle in a print statement must be a typeglob or a real scalar variable (not an indexed element of a hash/array). You can work around like you did or:

    print {$self->{_handle}} "$location, $message. \n";

    I guess the gurus familiar with Perl's guts can explain why. I can't.

    --bwana147

Re: dereferencing file handles
by ariels (Curate) on Sep 03, 2001 at 15:08 UTC
    From perldoc -f print:
    Note that if you're storing FILEHANDLES in an array or other expressio +n, you will have to use a block returning its value instead: print { $files[$i] } "stuff\n"; print { $OK ? STDOUT : STDERR } "stuff\n";
Re (tilly) 1: dereferencing file handles
by tilly (Archbishop) on Sep 03, 2001 at 18:28 UTC
    The problem is that print is using what is known as the "indirect object syntax", which requires a complex lookahead on Perl's part. The result is that complex operations that return a scalar are not going to do what you expect.

    See the section labelled WARNING in perlobj for details. As for solutions, you can use the one that you stumbled on of putting the filehandle into a scalar, the block syntax suggested by another person which looks like:

    print {$self->{_handle}} "string here";
    Or you can take a speed hit and use IO::Handle and then
    $self->{_handle}->print("string here");
    Note that the last causes a speed hit, and its internal implementation causes some issues that tye has a complex fix for.
      Thanks for this. I've changed the $fh thingy to what you've suggested. it makes more sense in the code to read...
      I guess the answer is "you have problems dereferencing".. looks like i may have to read more.

      this will end up a part of a web application, so i dont think the speed hit is really an option.. ;-)

      thanks again ++ for you.