Документация
HTML CSS PHP PERL другое

Section 10.18.  Autoflushing

 
Previous
Table of Contents
Next

10.18. Autoflushing

Avoid a raw select when setting autoflushes.

When it comes to maintainable code, it doesn't get much worse than this commonly used Perl idiom:

    select((select($fh), $|=1)[0]);

The evil one-argument form of select[*] takes a filehandle and makes it the (global!) default destination for print statements from that point onwards. That is, after a select, instead of writing to *STDOUT, any print statement that isn't given an explicit filehandle will now write to the filehandle that was select'd.

[*] As opposed to the evil four-argument select (see Chapter 8).

This change of default happens even if the newly selected filehandle was formerly confined to a lexical scope:

    for my $filename (@files) {
        # Open a lexical handle (will be automatically closed at end of iteration)
        open my $fh, '>', $filename
            or next;

        # Make it the default print target...
        select $fh;

        # Print to it...
        print "[This file intentionally left blank]\n";
    }

In actual applications, that last print statement would probably be replaced by a long series of separate print statements, controlled by some complex text-generation algorithm. Hence the desire to make the current $fh the default output filehandle, so as to avoid having to explicitly specify the filehandle in every print statement.

Unfortunately, because select makes its argument the global default for print, when the final iteration of the loop is finished, the last file that was successfully opened will remain the global print default. That filehandle won't be garbage-collected and auto-closed like all the other filehandles were, because the global default still refers to it. And for the remainder of your program, every print that isn't given an explicit filehandle will print to that final iterated filehandle, rather than to *STDOUT.

So don't use one-argument select. Ever.

And that appalling select statement shown at the start of this guideline?

    select((select($fh), $|=1)[0]);

Well, that's the "classic" way to make the filehandle in $fh autoflush; that is, to write out its buffer on every print, not just when it sees a newline. First, you select the filehandle you want to autoflush (select($fh)). Then you set the punctuation variable that controls autoflushing of the currently selected filehandle ($|=1). The sneaky bit is that you do those two things in a list ((select($fh), $|=1)), so their return values become the two values of that list. Because select returns the previous default filehandlethe one that you just replacedthat previous filehandle must now be the first element of the list. So if you index back into the list, requesting the first element ((select($fh), $|=1)[0]), you'll get back the previously selected filehandle. Then all you need to do is pass that filehandle to select again (select((select($fh), $|=1)[0])) to restore the original default, and your journey to the Dark Side will be complete[*].

[*] Once you start down that path, forever will it dominate your maintenance...confuse you, it will!

Fortunately, if you're using lexical filehandles, there's no need for this kind of necroselectomancy. Lexical filehandles act like fully-fledged objects of the IO::Handle class so, if you're willing to load the IO::Handle module, there's a much simpler method for setting their autoflush behaviour:


    use IO::Handle;

    
# and later...
$fh->autoflush( );

You can even use this same approach on the standard package-scoped filehandles:


    use IO::Handle;

    
# and later...
*STDOUT->autoflush( );

    Previous
    Table of Contents
    Next
    © 2000- NIV