⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch14_01.htm

📁 编程珍珠,里面很多好用的代码,大家可以参考学习呵呵,
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<tt class="literal">Tie::Scalar</tt> provides elemental methods that do verylittle, and <tt class="literal">Tie::StdScalar</tt> provides methods thatmake a tied scalar behave like a regular Perl scalar.  (Which seemssingularly useless, but sometimes you just want a bit of a wrapperaround the ordinary scalar semantics, for example, to count the numberof times a particular variable is set.)<a name="INDEX-2682"></a><a name="INDEX-2683"></a></p><p>Before we show you our elaborate example and complete description ofall the mechanics, here's a taste just to whet your appetite--and toshow you how easy it really is.  Here's a complete program:<blockquote><pre class="programlisting">#!/usr/bin/perlpackage Centsible;sub TIESCALAR { bless \my $self, shift }sub STORE { ${ $_[0] } = $_[1] }  # do the default thingsub FETCH { sprintf "%.02f", ${ my $self = shift } } # round valuepackage main;tie $bucks, "Centsible";$bucks = 45.00;$bucks *= 1.0715; # tax$bucks *= 1.0715; # and double tax!print "That will be $bucks, please.\n";</pre></blockquote>When run, that program produces:<blockquote><pre class="programlisting">That will be 51.67, please.</pre></blockquote>To see the difference it makes, comment out the callto <tt class="literal">tie</tt>; then you'll get:<blockquote><pre class="programlisting">That will be 51.66505125, please.</pre></blockquote>Admittedly, that's more work than you'd normally go through toround numbers.</p><h3 class="sect2">14.1.1. Scalar-Tying Methods</h3><p><a name="INDEX-2684"></a><a name="INDEX-2685"></a><a name="INDEX-2686"></a>Now that you've seen a sample of what's to come, let's develop a moreelaborate scalar-tying class.  Instead of using any canned package forthe base class (especially since scalars are so simple), we'll look ateach of the four methods in turn, building an example class named<tt class="literal">ScalarFile</tt>.  Scalars tied to this class containregular strings, and each such variable is implicitly associated witha file where that string is stored.  (You might name your variables toremind you which file you're referring to.)  Variables are tied to theclass this way:<blockquote><pre class="programlisting">use ScalarFile;       # load ScalarFile.pmtie $camel, "ScalarFile", "/tmp/camel.lot";</pre></blockquote>Once the variable has been tied, its previous contents are clobbered,and the internal connection between the variable and its objectoverrides the variable's normal semantics.  When you ask for the valueof <tt class="literal">$camel</tt>, it now reads the contents of<em class="emphasis">/tmp/camel.lot</em>, and when you assign a value to<tt class="literal">$camel</tt>, it writes the new contents out to<em class="emphasis">/tmp/camel.lot</em>, obliterating any previousoccupants.</p><p>The tie is on the variable, not the value, so the tied nature of avariable does not propagate across assignment.  For example, let's sayyou copy a variable that's been tied:<blockquote><pre class="programlisting">$dromedary = $camel;</pre></blockquote></p><p>Instead ofreading the value in the ordinary fashion from the<tt class="literal">$camel</tt> scalar variable, Perl invokes the<tt class="literal">FETCH</tt> method on the associated underlying object.It's as though you'd written this:<blockquote><pre class="programlisting">$dromedary = (tied $camel)-&gt;FETCH():</pre></blockquote><a name="INDEX-2687"></a>Or if you remember the object returned by <tt class="literal">tie</tt>, youcould use that reference directly, as in the following sample code:<blockquote><pre class="programlisting">$clot = tie $camel, "ScalarFile", "/tmp/camel.lot";$dromedary = $camel;          # through the implicit interface$dromedary = $clot-&gt;FETCH();  # same thing, but explicitly</pre></blockquote>If the class provides methods besides <tt class="literal">TIESCALAR</tt>,<tt class="literal">FETCH</tt>, <tt class="literal">STORE</tt>, and<tt class="literal">DESTROY</tt>, you could use <tt class="literal">$clot</tt> toinvoke them manually.  However, one normally minds one's own businessand leaves the underlying object alone, which is why you often see thereturn value from <tt class="literal">tie</tt> ignored.  You can still getat the object via <tt class="literal">tied</tt> if you need it later (forexample, if the class happens to document any extra methods you need).Ignoring the returned object also eliminates certain kinds of errors,which we'll cover later.</p><p>Here's the preamble of our class, which we will put into<em class="emphasis">ScalarFile.pm</em>:<blockquote><pre class="programlisting">package ScalarFile;use Carp;                # Propagate error messages nicely.use strict;              # Enforce some discipline on ourselves.use warnings;            # Turn on lexically scoped warnings.use warnings::register;  # Allow user to say "use warnings 'ScalarFile'".my $count = 0;           # Internal count of tied ScalarFiles.</pre></blockquote><a name="INDEX-2688"></a>The standard <tt class="literal">Carp</tt> module exports the<tt class="literal">carp</tt>, <tt class="literal">croak</tt>, and<tt class="literal">confess</tt> subroutines, which we'll use in the codelater in this section.  As usual, see <a href="ch32_01.htm">Chapter 32, "Standard Modules"</a>, or the online docs for moreabout <tt class="literal">Carp</tt>.</p><p>The following methods are defined by the class.</p><dl><dt><b><em class="replaceable">CLASSNAME</em><tt class="literal">-&gt;TIESCALAR(</tt><em class="replaceable">LIST</em><tt class="literal">)</tt></b></dt><dd><p>  The <tt class="literal">TIESCALAR</tt> methodof the class is triggered whenever you <tt class="literal">tie</tt> a scalarvariable.  The optional <em class="replaceable">LIST</em> contains anyparameters needed to initialize the object properly.  (In our example,there is only one parameter: the name of the file.) The method shouldreturn an object, but this doesn't have to be a reference to a scalar.In our example, though, it is.<blockquote><pre class="programlisting">sub TIESCALAR {           # in ScalarFile.pm    my $class    = shift;    my $filename = shift;    $count++;             # A file-scoped lexical, private to class.    return bless \$filename, $class;}</pre></blockquote>Since there's no scalar equivalent to the anonymous array and hashcomposers, <tt class="literal">[]</tt> and <tt class="literal">{}</tt>, we merelybless a lexical variable's referent, which effectively becomesanonymous as soon as the name goes out of scope.  This works fine (youcould do the same thing with arrays and hashes) as long as thevariable really is lexical.  If you try this trick on a global, youmight think you're getting away with it, until you try to createanother <em class="filename">camel.lot</em>.  Don't be tempted to writesomething like this:<blockquote><pre class="programlisting">sub TIESCALAR { bless \$_[1], $_[0] }    # WRONG, could refer to global.</pre></blockquote>A more robustly written constructor might check that the filename isaccessible.  We check first to see if the file is readable, since wedon't want to clobber the existing value.  (In other words, weshouldn't assume the user is going to write first.  They might betreasuring their old Camel Lot file from a previous run of theprogram.) If we can't open or create the filename specified, we'llindicate the error gently by returning <tt class="literal">undef</tt> andoptionally printing a warning via <tt class="literal">carp</tt>.  (We couldjust <tt class="literal">croak</tt> instead--it's a matter of taste whetheryou prefer fish or frogs.) We'll use the <tt class="literal">warnings</tt>pragma to determine whether the user is interested in our warning:<blockquote><pre class="programlisting">sub TIESCALAR {           # in ScalarFile.pm    my $class    = shift;    my $filename = shift;    my $fh;    if (open $fh, "&lt;", $filename or        open $fh, "&gt;", $filename)    {        close $fh;        $count++;        return bless \$filename, $class;    }    carp "Can't tie $filename: $!" if warnings::enabled();    return;}</pre></blockquote>Given such a constructor, we can now associate the scalar<tt class="literal">$string</tt> with the file<em class="filename">camel.lot</em>:</p><blockquote><pre class="programlisting">tie ($string, "ScalarFile", "camel.lot") or die;</pre></blockquote><p><a name="INDEX-"></a>(We're still assuming some things we shouldn't.  In a productionversion of this, we'd probably open the filehandle once and rememberthe filehandle as well as the filename for the duration of the tie,keeping the handle exclusively locked with <tt class="literal">flock</tt>the whole time.  Otherwise we're open to race conditions--see "TimingGlitches" in <a href="ch23_01.htm">Chapter 23, "Security"</a>.)</p></dd><dt><b><em class="replaceable">SELF</em><tt class="literal">-&gt;FETCH</tt></b></dt><dd><p>This method is invoked whenever you access the tied variable (that is,read its value).  It takes no arguments beyond the object tied to thevariable.  In our example, that object contains the filename.<blockquote><pre class="programlisting">sub FETCH {    my $self  = shift;    confess "I am not a class method" unless ref $self;    return unless open my $fh, $$self;    read($fh, my $value, -s $fh);  # NB: don't use -s on pipes!    return $value;}</pre></blockquote>This time we've decided to blow up (raise an exception) if<tt class="literal">FETCH</tt> gets something other than a reference.(Either it was invoked as a class method, or someone miscalled it as asubroutine.) There's no other way for us to return an error, so it'sprobably the right thing to do.  In fact, Perl would have raised anexception in any event as soon as we tried to dereference<tt class="literal">$self</tt>; we're just being polite and using<tt class="literal">confess</tt> to spew a complete stack backtrace onto theuser's screen.  (If that can be considered polite.)</p><p>We can now see the contents of <em class="filename">camel.lot</em> when we say this:<blockquote><pre class="programlisting">tie($string, "ScalarFile", "camel.lot");print $string;</pre></blockquote></p></dd><dt><b><em class="replaceable">SELF</em><tt class="literal">-&gt;STORE(</tt><em class="replaceable">VALUE</em><tt class="literal">)</tt></b></dt><dd><p><a name="INDEX-"></a>This method is run when the tied variable is set (assigned).  Thefirst argument, <em class="replaceable">SELF</em>, is as always the object associated with thevariable; <em class="replaceable">VALUE</em> is whatever was assigned to the variable.  (Weuse the term "assigned" loosely--any operation that modifiesthe variable can call <tt class="literal">STORE</tt>.)<blockquote><pre class="programlisting">sub STORE {    my($self,$value) = @_;    ref $self                   or confess "not a class method";    open my $fh, "&gt;", $$self    or croak "can't clobber $$self: $!";    syswrite($fh, $value) == length $value                                or croak "can't write to $$self: $!";    close $fh                   or croak "can't close $$self: $!";    return $value;}</pre></blockquote>After "assigning" it, we return the new value--because that's whatassignment does.  If the assignment wasn't successful, we<tt class="literal">croak</tt> out the error.  Possible causes might be thatwe didn't have permission to write to the associated file, or the diskfilled up, or gremlins infested the disk controller.  Sometimes youcontrol the magic, and sometimes the magic controls you.</p><p>We can now write to <em class="filename">camel.lot</em> when we say this:<blockquote><pre class="programlisting">tie($string, "ScalarFile", "camel.lot");$string  = "Here is the first line of camel.lot\n";

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -