Perl typeglobs and Real References: What will I get if I write in \ * STDOUT instead of * STDOUT?

What will I get if I write to a real link, for example \*STDOUT , instead of type glob, for example *STDOUT ?

+4
source share
4 answers

One of them is typeglob , the other is a link to it.

As far as I know, the main practical difference is that you DO NOT bless the typeglob in the object, but you CAN bless the typeglob reference (which makes IO::Handle ) p>

This distinction is discussed in detail in the Perl Cookbook, recipe 7.16. "Storing file descriptors in a variable."


Another difference is that the assignment of glob creates an alias for the ENTRE glob, while the assignment of the link glob does what is expected (as described in perldoc perlmod, "Symbol Tables" section . To illustrate:

 @STDOUT=(5); $globcopy1 = *STDOUT; # globcopy1 is aliased to the entire STDOUT glob, # including alias to array @STDOUT $globcopy2 = \*STDOUT; # globcopy2 merely stores a reference to a glob, # and doesn't have anything to do with @STDOUT print $globcopy1 "TEST print to globcopy1/STDOUT as filehandle\n"; print "TEST print of globcopy1/STDOUT as array: $globcopy1->[0]\n\n"; print $globcopy2 "TEST print to globcopy2/STDOUT as filehandle\n"; print "TEST print of globcopy2/STDOUT as array: $globcopy2->[0]\n\n"; 

It produces:

 TEST print to globcopy1/STDOUT as filehandle TEST print of globcopy1/STDOUT as array: 5 TEST print to globcopy2/STDOUT as filehandle Not an ARRAY reference at line 8. 

As a side note, the rumor that a link of type glob is the only way to pass a file descriptor to a function does not occur:

 sub pfh { my $fh = $_[0]; print $fh $_[1]; } pfh(*STDOUT, "t1\n"); pfh(\*STDOUT, "t2\n"); # Output: # t1 # t2 
+8
source

* indicates type glob, which is an entry in the character table. For instance:

 my $x; print *x; 

Let's say *main::x .

\ indicates the link. Try the following:

 use YAML::XS; print Dump *STDOUT; print Dump \*STDOUT; 

The first is the globe, the second is the link. Presumably when you do this:

 my $fh = *STDOUT; 

In fact, you are copying glob to the new record, although I do not think it is very important - if you are now close STDOUT , $fh also closes. It:

 my $fh = \*STDOUT; 

is just a reference, and which is preferable. See also:

http://perldoc.perl.org/perldata.html#Typeglobs-and-Filehandles

+4
source

You get a link to typeglob named STDOUT . See perlref .

Here's what he says about this:

It is not possible to create a true reference to an I / O descriptor (filehandle or dirhandle) using the backslash operator. The biggest one can get a link to typeglob, which is actually a complete entry in the character table. But see the Syntax Explanation * foo {THING} below. However, you can still use the glob and globrefs types as if they were I / O handles.

perldata is also helpful.

In conclusion, this is similar to creating a link to a file descriptor. You can use it as one. But with typeglob you can do other things too. The symbol table stores the values ​​of all variables named STDOUT , that is, $STDOUT , @STDOUT %STDOUT , %STDOUT and even &STDOUT , as well as a file descriptor. You can access them with just one type. But in the case of STDOUT you probably won't have to worry about this because you are unlikely to have %STDOUT in your code.

This kind of link is also the only way to pass a file descriptor as an argument to a function.

 sub myprint { my $fh = shift; print $fh "Hello World!\n"; } &myprint(\*STDOUT); 
+3
source

Perl provides 3 ways to get the same file descriptor:

  • *STDOUT - typeglob
  • \*STDOUT is a reference to typeglob
  • *STDOUT{IO} is either undef or a reference to IO :: Handle

Now I prefer to use \*STDOUT for consistency. When I use open(my $handle, ...) to automatically create a file descriptor, Perl gives me a link to typeglob. Therefore, if I want to use either STDOUT or an open file, I can write

 my $handle; if ($path eq "-") { $handle = \*STDOUT; } else { open($handle, '>', $path) or die "Can't open $path: $!"; } 

therefore, my descriptor is always a reference to the type glob. This consistency is not too important. The rest of my code does not care if it has a link to typeglob or some other file descriptor.

Given my preference for \*FH , I was surprised to find an example where *FH works, but \*FH fails. In perlsub, "Pass by Reference" , I find this example:

If you plan on creating new file descriptors, you can do this. Pay attention to transfer only naked * FH, not its link.

 sub openit { my $path = shift; local *FH; return open (FH, $path) ? *FH : undef; } 

I really need to pass *FH , not \*FH , although perlsub doesn't explain why. I checked this myself by calling sub in the script:

 use strict; use warnings; sub openit { my $path = shift; local *FH; return open (FH, $path) ? *FH : undef; } my $it = openit '</etc/fstab' or die "Can't open it: $!"; if (defined($_ = <$it>)) { print "Its first line is $_"; } else { print "It has no first line\n"; } 

When the return value is *FH , the script works. When I change it to \*FH , the script fails. Since I use warnings, I see the following:

 readline() on unopened filehandle FH at scratch.pl line 11. It has no first line 

This happened because \*FH points to a global variable. In sub, local *FH given a temporary value for the global type glob. When sub returns, *FH has lost its temporary value, and FH returned to the closed file descriptor.

The fix is ​​to copy the temporary value. This happens when sub returns *FH . It copies typeglob because it is not an lvalue sub. Now I know why Sub should return *FH , not \*FH .

If I want sub to return the link, I can copy the type glob and then take the link:

 local *FH; return open (FH, $path) ? \(my $glob = *FH) : undef; 

Or I can auto generate the link:

 my $globref; return open ($globref, $path) ? $globref : undef; 

If you change the value of STDOUT and you need to copy the old value, you must copy the type glob using *STDOUT , not \*STDOUT . You probably don't need to copy the value.

+1
source

Source: https://habr.com/ru/post/1411055/


All Articles