sgifford has asked for the wisdom of the Perl Monks concerning the following question:

I'm working on a module that prints output. I'd like to provide a printto method to print to a filehandle, and a print method to print to the currently selected filehandle. To avoid code duplication, I'd like to implement print as something like this:
sub print { printto(select,@_); }
But this doesn't work under use strict; it generates:
Can't use string ("main::STDOUT") as a symbol ref while "strict refs" +in use...
Here's a short example program that gives the same error:
#!/usr/bin/perl -w use strict; my $fh = select; print $fh "Hello\n";
Anybody have a better way of doing this? Or should I juse use no strict 'refs'?

Thanks!

Edit by tye, change PRE to CODE around long line

Replies are listed 'Best First'.
Re: Using currently selected filehandle
by particle (Vicar) on Jun 23, 2003 at 18:26 UTC
    #!/usr/bin/perl -w use strict; local *FH= select; print FH "Hello\n";

    ~Particle *accelerates*

      I'd call that a bug in use strict, to allow a symbolic glob ref like that. On the other hand, by the time you're playing with glob aliasing, what's a little symbolic reference between friends? :-,

          -- Chip Salzenberg, Free-Floating Agent of Chaos

        that's no bug, Chip. print accepts a filehandle as the first parameter. filehandles can be accessed as barewords, as they are in many places, such as open FH, '>', '/dev/null' or die $!;. filehandles are either referenced as barewords, or as foo-thing syntax: *FH{IO}.

        ~Particle *accelerates*

      Thanks, that's exactly what I was looking for.

      What's going on there? Is select being called in a typeglob context, and so returning a typeglob instead of a string?

        there is no typeglob context, but you're on to something. when you assign the result from select to a scalar, it stringifies the IO type that select normally returns. since there's no sigil for an IO type, you usually assign to it through a typeglob. since typeglobs cannot be lexical, you cannot use my to limit their scope. instead, you must use local, which will limit their scope to the current block, and create the typeglob, if it doesn't already exist.

        hope that helps.

        ~Particle *accelerates*

Re: Using currently selected filehandle
by tilly (Archbishop) on Jun 23, 2003 at 19:33 UTC
    Easy solution. Implement printto by temporarily selecting the handle you want to print to and then calling print. The print function just works straight.

    No typeglob tricks needed.

Re: Using currently selected filehandle
by hardburn (Abbot) on Jun 23, 2003 at 17:58 UTC

    I'm not sure why you're doing this. print can already print to a filehandle:

    print "Hello, world!\n"; select(STDERR); print "To STDERR\n"; select(STDOUT); print STDERR "Also on STDERR\n";

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    Note: All code is untested, unless otherwise stated

      printto is a fairly complex 30-line sub which prints to a filehandle. To use print's default behavior, I would have to duplicate that entire sub, changing print $fh ... to just print .... I want to avoid doing that; it would require any changes to the output routines to happen identically in both places to avoid inconsistency, and my experience is that this is error-prone.

      Of course, if anybody has suggestions for another way to do this, I'm all ears!

        why not have your print method take an optional filehandle?

        ~Particle *accelerates*

Re: Using currently selected filehandle
by erasei (Pilgrim) on Jun 23, 2003 at 18:17 UTC
    Or do it the other way around, with a combination of yours and hardburn's methods. Name your sub to be 'printto' and then print to the filehand in the sub, like this:
    my $fh = select; printto("Hello\n"); sub printto { my $string = shift; print $fh $string; }
    Although it just seems like it'd be a lot either to just print it to the file hand directly as hardburn said.
      If you add a use strict to the top of that, it doesn't work.