Typeglobs, Anonymous Filehandles

B

Bernard Chan

Hello,

I have finally been able to get an output buffering module I mentioned
in an earlier post to work. Though the module is complete and works as
expected, I'm still not sure how some of the code I wrote actually works.

I tried this: ====================

perl -Mstrict -e 'local *FH = "STDOUT"; print FH "Test Message\n"'

It seems like FH now points to the filehandle STDOUT with the
assignment. I forgot if anywhere in the perldoc mentioned this
behaviour, or I just got it "discovered" by accident. And also, isn't
this regarded as a symbolic reference, but Perl under strict 'refs'
doesn't complain?

Another mystery: ================

my $BUF;
open my $FH, ">", \$BUF;
print ref($FH)

outputs "GLOB".

From the perldocs my perception is that typeglobs are symbol table
entries. But now $FH is a lexical, so it is not on the symbol table.
Then where does the GLOB come from? What does it contain?

Now, $FH being a lexical, *$FH{GLOB} exists, but what is contained in
it? And *{$FH{GLOB}}{GLOB}, ... ?


Hope some brilliant minds here may give me some insights.

Regards,
Bernard Chan.
*** Free account sponsored by SecureIX.com ***
*** Encrypt your Internet usage with a free VPN account from http://www.SecureIX.com ***
 
U

Uri Guttman

BC> Hello,
BC> I have finally been able to get an output buffering module I mentioned
BC> in an earlier post to work. Though the module is complete and works as
BC> expected, I'm still not sure how some of the code I wrote actually
BC> works.

why don't you post the code for review?

BC> I tried this: ====================

BC> perl -Mstrict -e 'local *FH = "STDOUT"; print FH "Test Message\n"'

BC> It seems like FH now points to the filehandle STDOUT with the
BC> assignment. I forgot if anywhere in the perldoc mentioned this
BC> behaviour, or I just got it "discovered" by accident. And also,
BC> isn't this regarded as a symbolic reference, but Perl under strict
BC> 'refs' doesn't complain?

it is a symref in a way but it is not covered by strict. since early
perl days, file handles were strings and that is still supported. in
many places where you are allowed a file handle you can put a bareword
or a scalar string expression (you may need to wrap complex handle name
expressions with {} or assign them to a scalar var). the string is used
to lookup up the handle in the symbol table and that has a glob which is
used as the real handle. in any case the best way to do that is:

local *FH = \*STDOUT ; print FH "Test Message\n"'

that assignment is clean and doesn't use any symrefs. but even so, there
is no need for the local if you just used a scalar var to hold the
handle.

my $fh = \*STDOUT ; print $fh "Test Message\n"'

BC> Another mystery: ================

BC> open my $FH, ">", \$BUF;
BC> print ref($FH)

BC> outputs "GLOB".

BC> From the perldocs my perception is that typeglobs are symbol table
BC> entries. But now $FH is a lexical, so it is not on the symbol
BC> table. Then where does the GLOB come from? What does it contain?

a glob is just a special data structure used internally by perl. the
symbol table is made of globs but they can be used anywhere. a glob is a
simple structure with entries (the docs have the details) for the
various things that perl can store in a symtable entry. these include
scalar, array, hash, code and an i/o handle. there is no other way to
access a perl i/o handle except through a glob. all of the other types
can have refs taken of them but the best a handle can do is a ref to the
glob that has that handle. perl knows that if it sees a glob in i/o
handle spots, it uses the handle slot of that glob for the i/o handle.

BC> Now, $FH being a lexical, *$FH{GLOB} exists, but what is contained
BC> in it? And *{$FH{GLOB}}{GLOB}, ... ?

well, did you take a look and see? as i noted above, single globs
generally aren't going to be made into trees. regular refs can do that
and without all those extra glob layers in between. this is another
reason why i preach to not use symrefs as they just use the symtable as
a tree structure but it is slower, bulkier, global, and
dangerous. effectively symrefs are just syntactic sugar that makes the
symtree look like a ref based hash tree.

uri
 
B

Bernard Chan

Here is what I have now:
===================================================================

# PHP has some output buffering functions. Perl should have some too.
# Here is a crude implementation utilizing the Perl open() of
scalar-backed filehandles.
# In fact, this is likely to be more flexible as we can apply
# this to any output stream (including files, probably sockets too, etc.).

package IO::OutputBuffer;

use Encode;
use strict;

# Initialize output buffer
# Defaults to the currently selected filehandle (usually STDOUT).
# $obj = IO::OutputBuffer->new(FILEHANDLE)
sub new {
my ($ref, $io_orig) = @_;
my $pkg = ref($ref) || $ref;
$io_orig ||= select();
{
no strict 'refs';
# If passed in something like 'STDOUT' we will take
# a GLOB reference right away.
# But we don't accept *STDOUT, *STDOUT{IO} or something like that.
$io_orig =
(ref($io_orig) eq 'GLOB' && $io_orig) ||
\*$io_orig;
}

my $buf = '';
my $this = bless {
'IO_REF' => \*$io_orig, # Take another pointer to original GLOB
'IO_BUF' => undef, # GLOB to scalar-backed filehandle
'IO_ORIG' => *$io_orig, # Original GLOB
'BUF' => \$buf, # Buffer
}, $pkg;
return $this;
}

# Start output buffering on given filehandle.
# $buffer->start()
sub start {
my $this = shift;

my $io_new;
open $io_new, ">:utf8", $this->{'BUF'};

$this->{'IO_BUF'} = *$io_new;
# Modify the original glob to point to new filehandle
*{ $this->{'IO_REF'} } = $io_new;
}

# Retrieve the content in the buffer saved during buffering.
# $bufferedContent = $buffer->getContent()
sub getContent {
my $this = shift;
return ${$this->{'BUF'}};
}

# Clean the buffer.
# $bufferedContent = $buffer->clean()
sub clean {
my $this = $_[0];
my $content = &getContent;
local *MEM = getBufferedFilehandle($this);
${ $this->{'BUF'} } = '';
seek MEM, 0, 0 if (*MEM); # Reset filehandle's position after we have
reset the buffer
return $content;
}

# Send the content of the output buffer to the underlying data stream.
# The buffer will be cleaned up.
# $buffer->flush();
sub flush {
my $this = $_[0];
my $content = &clean;
$content = decode_utf8($content);
local *ORIG = getUnbufferedFilehandle($this);
print ORIG $content;
}

# Retrieve a filehandle to the original, unbuffered data stream.
# This is useful if we really have something to write to the underlying
stream without
# terminating output buffering.
# $fh = $buffer->getUnbufferedFilehandle()
sub getUnbufferedFilehandle {
my ($this) = @_;
return $this->{'IO_ORIG'};
}

# Retrieve a filehandle to the in-memory data stream.
# $fh = $buffer->getBufferedFilehandle()
sub getBufferedFilehandle {
my ($this) = @_;
return $this->{'IO_BUF'};
}

# Stop output buffering for filehandle saved in the given ticket.
# Restore the originally saved filehandle.
# $buffer->end();
sub end {
my ($this) = @_;
my ($io, $io_orig) = ($this->getBufferedFilehandle(),
$this->getUnbufferedFilehandle());
# Restore the filehandle in the GLOB
*{$this->{'IO_REF'}} = $io_orig;
}

1;
*** Free account sponsored by SecureIX.com ***
*** Encrypt your Internet usage with a free VPN account from http://www.SecureIX.com ***
 
B

Bernard Chan

Thank you very much for your clarification.

Uri said:
why don't you post the code for review?

Just thought nobody would be interested in it. Posted.
Comments/corrections etc. appreciated.
BC> Now, $FH being a lexical, *$FH{GLOB} exists, but what is
contained BC> in it? And *{$FH{GLOB}}{GLOB}, ... ?

well, did you take a look and see?

Tried print()ing the globs. All give "*main::$FH". Not totally sure what
that means though, especially in the context of a lexical variable.

I don't use globs or symrefs either normally. I was just curious to find
out more about them, especially in places where the docs appear a bit
confusing.

Regards,
Bernard Chan.
*** Free account sponsored by SecureIX.com ***
*** Encrypt your Internet usage with a free VPN account from http://www.SecureIX.com ***
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top