• Aucun résultat trouvé

A threaded pager class

Dans le document Object Oriented Perl (Page 184-188)

Blessing other things

5.3 B LESSING A TYPEGLOB

5.3.3 A threaded pager class

my $PAGER = IO::Pager->new();

# Any print to $PAGER in the rest of the block will be paged

# For example:

foreach my $i (1..100) {

print $PAGER "$i\n"; # Each line is paged through &IO::Pager::_page }

}

print "done\n"; # Unpaged

Note in particular that, because the paging typeglob is now blessed into a class with a de-structor, there is no need to remember the vital call to close. It is now automatically invoked by the object’s destructor when the reference finally goes out of scope.

5.3.3 A threaded pager class

With a little more effort, the IO::Pager class could use Perl’s built-in threads,6 rather than sep-arate O/S processes. Figure 5.6 illustrates the variation. Naturally, this version of the class pro-vides exactly the same interface as the nonthreaded version.

package IO::Pager;

$VERSION = 2.00;

use strict;

use Carp;

use Symbol;

use Thread 'async';

sub new {

my $class = shift;

my %args = (lines=>23, prompt=>"--More--", endprompt=>"--No more--", @_);

my ($READHANDLE, $WRITEHANDLE) = (gensym, gensym );

pipe $READHANDLE, $WRITEHANDLE or die;

my $self = bless $WRITEHANDLE, $class;

my $thread_ref = async { _page($READHANDLE,%args) };

*$self = \$thread_ref;

return $self;

}

sub _page

6 Perl’s thread facilities are still experimental. The thread-specific details in this section may have changed if you’re using a version of Perl later than 5.005.

Listing 5.6 The Pager class (threaded)

{

my ($input,%args) = @_;

while (readline *$input) {

print;

_prompt($args{prompt}) || last unless $. % $args{lines};

}

_prompt($args{endprompt});

}

sub _prompt {

print $_[0];

return (<> !~ /^q/i); # Return false if user types 'q' }

sub close {

close $_[0];

${*{$_[0]}}->join;

}

sub print {

my ($self) = @_;

print $self (@_);

}

sub DESTROY {

$_[0]->close;

}

1;

The overall structure and operation of the threaded IO::Pager class is unchanged from the multiprocess version. It still connects the two parallel flows of control, the main program and the pager, with a pipe, but now the pipe is explicitly created by calling the built-in pipe func-tion on two anonymous typeglobs.

The typeglob storing the writable end of the pipe is immediately blessed as the pager ob-ject. The readable end of the pipe is passed to _page, which is invoked in a separate thread via the Thread::async subroutine. Thread::async returns a reference to a Thread object, temporarily stored in the lexical $thread_ref.

This Thread object will be needed later, so we need to keep it warm and dry in the in-terim. The SCALAR slot of the now-blessed typeglob, $self, is the ideal place. The assignment that accomplishes this, *$self = \$thread_ref, may look arcane, but it’s just a standard

typeglob assignment, placing a reference into a scalar to the appropriate slot of the typeglob referred to by $self.

Both the writable end of the interthread pipe and the reference to the pager thread itself are now stored in the one blessed typeglob, which is finally returned to the main program.

Meanwhile the _page subroutine is running in a separate thread, waiting to read lines from the readable end of the pipe (note: not from STDIN in this version). Once again it prints batches of lines and issues a prompt. Because the paging thread takes its input directly from the pipe, STDIN is still attached to the keyboard, so the threaded version of _prompt can obtain feedback with a simple diamond operation (<>).

The only other difference in the threaded version is in the IO::Pager::close method.

It still performs a built-in close on the pipe, thereby sending the pager thread the necessary end-of-file. However, because the pager is no longer running in a child process, the close doesn’t issue an implicit wait to allow the pager to finish.

Instead, we need to tell the main thread to pause until the pager thread finishes. This is done by calling the Thread::join method on the Thread object representing the pager thread. Fortunately, the constructor cached a reference to that Thread object in the scalar slot of the blessed typeglob. All we need to do is access that reference and call join through it.

Accessing the reference is a little ugly, as we have to write: ${*{$_[0]}}. This means:

take the first argument, $_[0], dereference it to a typeglob, *{$_[0]}, and select the scalar slot, ${*{$_[0]}}. The curly braces are required because, without them, $*$_[0] would be interpreted as $* $_[0], which is both invalid (missing operator before $_[0]), and deplorable (use of $* variable is deprecated). Fortunately, since the visually offensive, but syntactically ac-ceptable, version is hidden away in a method, no one else need ever know our shame.

5.3.4 Where to find out more

Typeglobs and their filehandles are challenging to understand, but are discussed at length in chapter 2. They are also widely mentioned in the standard Perl documentation in the files:

perldata (Typeglobs and Filehandles), perlmod (Symbol Tables), perlop (I/O Operators), perlref (Making References), and perlsub (Passing Symbol Table Entries). For an excellent tutorial on typeglobs, see chapter 3 of Advanced Perl Programming.

Threads are a recent and still experimental addition to Perl. The standard perl documen-tation perlthrtut provides an good overview. The specifics of their use are described in the doc-umentation of the Thread.pm module.

5.4 S UMMARY

• The qr operator builds a precompiled regular expression. Such regular expressions may be blessed as objects.

• Blessing a regular expression makes it possible to build efficient object-oriented pattern matchers, possibly with different semantics from Perl’s built-in regular expressions.

• A subroutine may also be blessed as an object. Such subroutines will typically be eval’d into existence according to a predefined template.

• Alternatively, blessed subroutines may be created as anonymous closures, to restrict access to lexical variables.

• A typeglob is really just a special type of container, and may also be blessed. Typically, this is done so that the filehandle within the typeglob can be used as an object.

C H A P T E R 6

Inheritance

Dans le document Object Oriented Perl (Page 184-188)