Eli-Sko has asked for the wisdom of the Perl Monks concerning the following question:

Gentle beings!

My perl 5.8.8 on SunOS' version of perldata claims:

All functions that are capable of creating filehandles (...) automatically create an anonymous filehandle if the handle passed to them is an uninitialized scalar variable.
Imagine, if you will, my consternation at encountering the following error:
Use of uninitialized value at releases.pl line 63. Can't use an undefined value as filehandle reference at releases.pl li +ne 63.
in response to this code snippet:
sub RLS_ropen($) { (@_ == 1) || die "RLS_ropen called w/ invalid argument list: ".sca +lar(@_)." params instead of 1"; my $name = $_[0]; unless ( $name ) { # invalid - null or empty file name return ""; } unless ( -e $name && -r $name ) { # file doesn't exits or is unreadable return ""; } open( my $dbh, "< $name"); return $dbh; } # end RLS_ropen
where line 63 is (of course) open( my $dbh, "< $name"); almost straight out of the perldoc example.

Please, someone, anyone, unperplex me!

Replies are listed 'Best First'.
Re: Uninitialized filehandles not as advertised?!
by almut (Canon) on May 26, 2008 at 16:45 UTC

    Are you sure you're calling the right Perl version? Grepping through the 5.8.8 Perl sources doesn't locate any such message, so it's unlikely it's issued by this version of Perl.  OTOH, Solaris' system Perl is typically ancient (e.g. 5.005_03 on Solaris 8), so my guess would be that you're inadvertently calling this system Perl...

    5.005_03

    $ /usr/bin/perl -e 'open my $fh, "uname -rs |"; print <$fh>;' Can't use an undefined value as filehandle reference at -e line 1.

    5.8.8

    $ /usr/local/bin/perl -e 'open my $fh, "uname -rs |"; print <$fh>;' SunOS 5.8
      Thank you, almut, this is indeed the case!

      I have no control over the environment here, and the sysadmins have chosen to play games with which perl my she-bang invokes, so that it varies from environment to environment in this multi-platform shop.

      Once you confirmed that I wasn't going nuts, then I knew that this HAD to be the problem, and I confirmed it. I have since developed a workaround which I intend to be the subject of my next posting.

      Thanks again!Eli-Sko

Re: Uninitialized filehandles not as advertised?!
by Corion (Patriarch) on May 26, 2008 at 16:39 UTC

    Works for me - maybe the problem is that you didn't show us where line 63 is in your code?

    I used the following code and it runs without problems:

    use strict; sub RLS_ropen($) { (@_ == 1) || die "RLS_ropen called w/ invalid argument list: ".sca +lar(@_)." params instead of 1"; my $name = $_[0]; unless ( $name ) { # invalid - null or empty file name return ""; } unless ( -e $name && -r $name ) { # file doesn't exits or is unreadable return ""; } open( my $dbh, "< $name"); return $dbh; } # end RLS_ropen my $fh = RLS_ropen "test.tmp";

    ... but maybe you want to be more explicit about things going bad, like for example, the file not existing:

    open( my $dbh, "< $name") or die "Couldn't open database file '$name': $!";

      maybe the problem is that you didn't show us where line 63 is in your code?

      Ahhh, so sorry worthy Corion, but immediately after the <readmore> block, I stated:
      where line 63 is (of course) open( my $dbh, "< $name"); almost straight out of the perldoc example.

      ...maybe you want to be more explicit about things going bad....

      The reason I just return instead of dying is that this is an interface, and the caller can use this routine to probe different paths / filenames until it finds the right one.

      And should you ask why I don't implement the search in the library, there are two answers: 1) because the caller needs to know where the data file is found, so that it can use that path to seek other files, as well, and 2) different programs calling this abstraction layer will have different search requirements.

      See? It all makes sense if you look at it carefully.

Re: Uninitialized filehandles not as advertised?!
by ikegami (Patriarch) on May 26, 2008 at 19:24 UTC

    Since open already checks if the file exists and if it's readable, your sub would be better written as

    sub RLS_ropen($) { my ($name) = @_; defined($name) or die "A file name must be supplied to RLS_ropen\n"; open(my $dbh, '<', $name) or return ''; return $dbh; } # end RLS_ropen

    And it begs the question... Why do you need a sub at all? All there is in it is the call to open

    Returning undef would be better than returning an empty string.

      And die-ing on a failed open call would probably be better than returning something that some other piece of code may try to use as a filehandle and mysteriously barf on.
        Not at all. It's different, not better or worse. For example, open returns an error code and not an exception. Why should his open wrapper be any different?

      ... Why do you need a sub at all?

      This code is part of an abstraction layer. Multiple programs will call this routine, which during the prototyping phase will just scan a flat file, but in future may use DBM or SQLite. As with abstraction layers in general, we won't want at that point to go back and change all of the open() calls to deal with database setup and take down.


      Returning undef would be better than returning an empty string.

      Answer 1

      I am lazy and prefer to use the simpler test with || instead of having to get my return value, separately test it for defined(), and only then start using it.

      Answer 2

      TMTOWTDI and I like it that way.

        Either || works for both undef and the empty string, or it works for neither. In this case, it works for both.

        TMTOWTDI means you have the freedom to choose the best way in a given situation. It doesn't mean it's a good idea to arbitrarily pick a way of doing something, contrary to the meaning you gave it. Returning undef results in a much better failure mode (gives an error with a meaningful message instead of a misleading warning) if the user forgets to do error checking, so returning undef is better than returning an empty string in this situation.