The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

IO::WrapTie - wrap tieable objects in IO::Handle interface

SYNOPSIS

First of all, you'll need tie(), so:

   require 5.004;

Use this with any existing class...

   use IO::WrapTie;
   use FooHandle;         # this is *not* an IO::Handle subclass (see below)
    
   # Assuming we want a "FooHandle->new(&FOO_RDWR, 2)", we can instead say...
   $FH = IO::WrapTie->new('FooHandle', &FOO_RDWR, 2);
   
   # Look, ma!  It works just like a real IO::Handle!  
   print $FH "Hello, ";            # traditional indirect-object syntax
   $FH->print("world!\n");         # OO syntax
   print $FH "Good", "bye!\n";     # traditional 
   $FH->seek(0, 0);                # OO
   @lines = <$FH>;                 # traditional (get the picture...?)

Or inherit from it to get a nifty new_tie() constructor...

   package FooHandle;     # this is *not* an IO::Handle subclass (see below)
   use IO::WrapTie;
   @ISA = qw(IO::WrapTie::Mixin);
   ...
   
   package main;    
   $FH = FooHandle->new_tie(&FOO_RDWR, 2);
   print $FH "Hello, ";            # traditional indirect-object syntax
   $FH->print("world!\n");         # OO syntax
    

DESCRIPTION

Suppose you have a class FooHandle, where...

  • FooHandle does not inherit from IO::Handle; that is, it performs filehandle-like I/O, but to something other than an underlying file descriptor. Good examples are IO::Scalar (for printing to a string) and IO::Lines (for printing to an array of lines).

  • FooHandle implements the TIEHANDLE interface (see perltie); that is, it provides methods TIEHANDLE, GETC, PRINT, PRINTF, READ, and READLINE.

  • FooHandle can be used in an ordinary OO-ish way via conventional FileHandle- and IO::Handle-compatible methods like getline(), read(), print(), seek(), tell(), eof(), etc.

Normally, users of your class would have two options:

  • Use only OO syntax, and forsake named I/O operators like 'print'.

  • Use with tie, and forsake treating it as a first-class object (i.e., class-specific methods can only be invoked through the underlying object via tied()... giving the object a "split personality").

But now with IO::WrapTie, you can say:

    $W = IO::WrapTie->new('FooHandle', &FOO_RDWR, 2);
    $W->print("Hello, world\n");   # OO syntax
    print $W "Yes!\n";             # Named operator syntax too!
    $W->weird_stuff;               # Other methods!

And if you're providing such a class, just inherit from IO::WrapTie::Mixin and that first line becomes even prettier:

    $FH = FooHandle->new_tie(&FOO_RDWR, 2);

The bottom line: now, almost any class can look and work exactly like an IO::Handle... and be used both with OO and non-OO filehandle syntax.

NOTES

Why not simply use the object's OO interface? Because that means forsaking the use of named operators like print(), and you may need to pass the object to a subroutine which will attempt to use those operators:

    $O = FooHandle->new(&FOO_RDWR, 2);
    $O->print("Hello, world\n");  # OO syntax is okay, BUT....
    
    sub nope { print $_[0] "Nope!\n" }
 X  nope($O);                     # ERROR!!! (not a glob ref)
    

Why not simply use tie()? Because (1) you have to use tied() to invoke methods in the object's public interface (yuck), and (2) you may need to pass the tied symbol to another subroutine which will attempt to treat it in an OO-way... and that will break it:

    tie *T, 'FooHandle', &FOO_RDWR, 2; 
    print T "Hello, world\n";     # Operator is okay, BUT... 
    
    tied(*T)->other_stuff;        # yuck! AND...
    
    sub nope { shift->print("Nope!\n") }
 X  nope(\*T);                    # ERROR!!! (method "print" on unblessed ref)

Why not simply write FooHandle to inherit from IO::Handle? I tried this, with an implementation similar to that of IO::Socket. The problem is that the whole point is to use this with objects that don't have an underlying file/socket descriptor.. Subclassing IO::Handle will work fine for the OO stuff, and fine with named operators if you tie()... but if you just attempt to say:

    $IO = FooHandle->new(&FOO_RDWR, 2);
    print $IO "Hello!\n";

you get a warning from Perl like:

    Filehandle GEN001 never opened

because it's trying to do system-level i/o on an (unopened) file descriptor. To avoid this, you apparently have to tie() the handle... which brings us right back to where we started! At least the IO::WrapTie mixin lets us say:

    $IO = FooHandle->new_tie(&FOO_RDWR, 2);
    print $IO "Hello!\n";

and so is not too bad. :-)

WARNINGS

Be aware that new_tie() always returns an instance of IO::WrapTie... it does not return an instance of the i/o class you're tying to! All OO-like use of this IO::WrapTie object is handled by the AUTOLOAD method, which for each message msg simply creates a "delegator" method IO::WrapTie::msg that passes msg on to the back-end object... so it looks like you're manipulating a "FooHandle" object directly, but you're not.

I have not explored all the ramifications of this use of tie(). Here there be dragons.

AUTHOR

Eryq (eryq@zeegee.com). President, Zero G Inc (http://www.zeegee.com).