Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Blessables -- What Can You Make Into Objects?

by chromatic (Archbishop)
on Apr 20, 2000 at 23:52 UTC ( [id://8259] : perltutorial . print w/replies, xml ) Need Help??


Creating a Perl object is tremendously easy -- you just bless a reference. While most objects you'll run across are built out of anonymous hashes, there are (at least) six other things you can bless. Here's a list of the whats, the hows, and at least some of the whys.


It's surprisingly easy to build a workable class out of an array. There are two strong benefits and one minor drawback to this. On the plus side, internal access performance improves as the overhead of hashing a key and doing a lookup on the key is avoided. Another benefit is that data is more protected via encapsulation (a user of your class will have a more difficult time autovivifying a data member of your class accidentally or purposefully with an array). The drawback is that you will have to know which data member is stored in which slot of the array. Here's an example:
package Soldier; use constant NAME => 0; use constant RANK => 1; use constant SERIAL => 2; sub new { my $class = shift; $class = ref($class) || $class; my $self = [ "", "Private", 000 ]; bless($self, $class); return $self; } sub name { my $self = shift; if (@_) { $self->[NAME] = shift; } return $self->[NAME]; } # similar methods for RANK and SERIAL


An excellent example provided in Damian Conway's Object Oriented Perl is that of an object oriented password. All that is necessary in the way of data is the password string, so why not use a simple scalar?
package Password; use Digest::MD5 qw(md5_base64); # based on an idea by jbontje sub new { my $class = shift; $class = ref($class) || $class; my $self = shift; # takes a cleartext string for the passwo +rd $self = md5_base64($self); bless($self, $class); return $self; } sub verify { my $self = shift; my $candidate = shift; return ($self eq md5_base64($candidate)); }
Any cryptographic implementation would do -- in fact, one could extend this class to use a different technique, if desires. Simply add an encrypt() method, and call that in new() and verify().

Regular Expressions

Perl 5.005's new qr{} operand allows regular expressions to be precompiled. (Maybe someday s{}{} will work for substitutions. I thought it might when I first prepared this tutorial, but it doesn't.) They're now fair game for blessing. One might code a word search problem something like this:
package WordMatch; sub new { my $class = shift; $class = ref($class) || $class; my $word = shift; # word to match my $self = qr/\[$word\]/; bless($self, $class); return $self; } sub match { my $self = shift; my $string = shift; return $string =~ $self ? "Matches!" : "Doesn't match!"; } package main; my $wm = WordMatch->new("hi"); print $wm->match("[hi] how are you?"), "\n"; print $wm->match("hi how are you?"), "\n";


This is the method used in many IO:: packages, such as IO::File. The reason for blessing a typeglob is usually to get at the filehandle associated with that typeglob. We might build a file reader like this:
package ReadFile; use Symbol; sub new { my $class = shift; $class = ref($class) || $class; my $file = shift; my $self = gensym; open ($self, $file) || die; bless($self, $class); return $self; } sub read_record { my $self = shift; local $/ = shift || $/; my $line = <$self>; return $line; }
To test it, you might use:
my $file = ReadFile->new(""); print $file->read_record("package"); print $file->read_record();
The constructor takes the name of the file to open, and the read_record() method takes an alternate input record separator. (Symbol::gensym returns a reference to an anonymous typeglob -- if you were wondering.) It would be good to add a DESTROY method to close the typeglob:
sub DESTROY { my $self = shift; close $self; print "Closed!\n"; # just to prove that it's working }


Consider a subroutine browser. Delving deeply into your source code, you might be able to extract argument lists and return values, if you comment diligently and intelligently:
package SubBrowser; sub new { my $class = shift; $class = ref($class) || $class; my $package = shift; my $subname = shift; # assume $package is a reference to a hash of in-memory source cod +e, # keyed by subroutine name my $sub; eval { $sub = $package->{$subname}; }; # ooh, tricky bless($sub, $class); return $sub; } sub display_args { # get data from $package } sub display_returns { # get data from $package } sub test { my $self = shift; return &$self; }
The methods display_args() and display_returns() can search through the comments at the start of the sub, matching however you mark the arguments passed and the return values. Build a hash of an object for each subroutine in your project, and you can iterate through them quickly and easily. This might be part of your test suite.


At the most bizarre end of the scale, one can even bless a reference to an object. Here's a quick example showing what can be done with our WordMatch object above:
package Wrapper; use vars '$AUTOLOAD'; sub new { my $class = shift; $class = ref($class) || $class; my $obj = shift; my $self = \$obj; bless($self, $class); return $self; } sub AUTOLOAD { my $self = shift; return if $AUTOLOAD =~ /::DESTROY/; $AUTOLOAD =~ s/.*:://; no strict 'refs'; print "Do something interesting here.\n"; return $$self->$AUTOLOAD(@_); } package main; # as before my $wm = WordMatch->new("hi"); print $wm->match("[hi] how are you?"), "\n"; print $wm->match("hi how are you?"), "\n"; # our new version my $wm2 = Wrapper->new(WordMatch->new("Hello")); print $wm2->match("[Hello] little girl"), "\n";
What does this give us over inheritance? Not a whole lot, at first glance. The AUTOLOAD mechanism uses symbolic references and has a performance penalty. (This can be alleviated somewhat by mucking about in the symbol table.)

What if the Wrapper class enforced security, though? If the only interface to an otherwise-private-only-by-politeness object were through this Wrapper class, no one would be able to reach inside the object and do bad things. We could hold a reference to the wrapped object in a variable declared in the use vars statement, making it accessible only through the defined Wrapper interface. We could write some an exception handling wrapper around modules known to be buggy or insecure.

You could do lots of things.... You may never need any of these trickeries, but aren't you glad to know they're available?

Replies are listed 'Best First'.
RE: Blessables -- What Can You Make Into Objects?
by perlmonkey (Hermit) on Apr 21, 2000 at 05:30 UTC
    This was a cool article. Dont know how useful, but very interesting.
    I am sure I will find a use for it somewhere.

    I tried to play with blessing RegEx's and ran into trouble.
    You mention that you might be able to bless a s{}{}, but I
    couldn't figure it out.

    I wanted an object that would do "s/^\s+|\s+$/g" for me.
    It would not compile if i did somethine like
    $self = s{^\s+|\s+$}{}; so I ended up using qr{} instead,
    putting the s/// in the member function:
    package Cleaner; sub new { my $class = shift; $class = ref($class) || $class; my $self = qr{^\s+|\s+$}; return bless($self, $class); } sub clean { $_[1] =~ s/$_[0]//g; } package main; $c = new Cleaner; $foo = "\t\tFOO\t\t"; $c->clean($foo); print $foo, "\n";
    So this works, but I dont know it is the most optimized
    routine. I did some benchmarks:
    package main; $c = new Cleaner; $foo = "\t\tFOO\t\t"; use Benchmark; timethese(1000000, { 'blessed' => sub { my $bar = $foo; $c->clean($bar) }, 'normal' => sub { my $bar = $foo; $bar =~ s/^\s+|\s$//g }, });
    And got these results:
    Benchmark: timing 1000000 iterations of blessed, normal... blessed: 43 wallclock secs (36.50 usr + 0.14 sys = 36.64 CPU) normal: 28 wallclock secs (24.19 usr + 0.08 sys = 24.27 CPU)
    I guess the difference is just the function call, but thought maybe there is a faster way. Any Ideas?
      Looks like I'm wrong about being able to bless a s{}{} statement. That would be truly convenient. I'll change that in the tutorial.

      In your benchmarking, lots of the time *is* taken up by the function call. Here's what I ran to prove that. (This has to be done in the Cleaner package, for obvious reasons.):

      timethese(1000000, { 'blessed' => sub { my $bar = $foo; $c->clean($foo); }, 'normal' => sub { my $bar = $foo; $bar =~ s/^\s+|\s$//g; }, 'super' => sub { my $bar = $foo; $bar =~ s/$c//g; }, });
      There's not nearly the performance penalty involved:
      Benchmark: timing 1000000 iterations of blessed, normal, super... blessed: 10 wallclock secs ( 9.83 usr + 0.00 sys = 9.83 CPU) normal: 4 wallclock secs ( 5.42 usr + 0.00 sys = 5.42 CPU) super: 7 wallclock secs ( 6.71 usr + 0.00 sys = 6.71 CPU)
      I suspect that if the regex were more complicated (and $foo were much longer), the difference wouldn't be quite as intense.
Re: Blessables -- What Can You Make Into Objects?
by demerphq (Chancellor) on Apr 26, 2002 at 20:57 UTC
    In your tutorial you mention blessing a precompiled regex.
    my $self = qr/\[$word\]/; bless($self, $class);
    My question is how does one then (cleanly) determine that the object is indeed a blessed precompiled regex?

    Oh and I already asked on p5p without much results and also here at the monastery


    Yves / DeMerphq
    Writing a good benchmark isnt as easy as it might look.

      You could overload stringification in your object that blesses the regexp.
      package Blessed::Re; use overload q{""} => 'stringify'; sub new { my $class = shift; my $word = shift; my $self = qr/\[$word\]/; return bless $self, $class; } sub stringify { my $self = shift; my $string = overload::StrVal $self; $string =~ s/SCALAR/Regexp/; return $string; } package main; my $x = Blessed::Re->new('word'); print "Class: ",ref $x,"\n"; print "Type: ", $x =~ /=(\w+)\(/, "\n";

      On the other hand, you did say "cleanly" ...

        Actually stringifying a regex does not return a string that contains "Regexp". It will look like any other scalar reference that has been stringified. (Youre thinking of what ref() would return.)

        Thats part of the problem...


        Yves / DeMerphq
        Writing a good benchmark isnt as easy as it might look.

Re: Blessables -- What Can You Make Into Objects?
by demerphq (Chancellor) on Apr 26, 2004 at 15:32 UTC

    As of Perl 5.7 you can bless FORMAT refs as well. :-)

    Oh, in a fit of not at all subtle advertising the ONLY module currently available that will dump them ALL correctly is Data::Dump::Streamer. Try it on a blessed regex that has stringification overloaded and itll still do the right thing. :-)


      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi