I wrote the following module but am not sure how to refer to the "last" and "head" nodes. As well as storing the address of the next node in "{nextNode}" in the previous node.
I am trying to save the reference of the class when storing it but later it's complaining: "Not a HASH reference at List.pm"; which I understand why but am not sure how the syntax would be.
If I de-reference $head and $last ($$last->{nextNode} = \$class) then I think it's using the actual name of my class; List and not the previous object like I want to.
package List;
my $head = undef;
my $last = undef;
sub new {
my $class = shift;
# init the head of the list
if ($head == undef) {
$head = \$class;
print "updated head to:$head", "\n";
}
$last = \$class;
$last->{nextNode} = \$class; # update previous node to point on this new one
print "updated last to:$last", "\n";
my $self = {};
$self->{value} = shift;
$self->{nextNode} = ""; # reset next to nothing since this node is last
return bless $self, $class;
}
Thanks guys
You should be storing $self everywhere instead of \$class. Storing $class is simply storing the name of the class, not the object itself.
Also, for $self->{nextNode} I'd store an undef instead of a blank string. Or better yet, simply don't create it at all and use exists when checking if it is there.
You're over thinking it. If you use an array for your list instead of a hash, you don't need to worry about the head and last. The head of an array is $array[0] and the last member is $array[-1]. Simple and easy to do.
Here's a quick standard class definition for defining a list. I've only defined a constructor (the new subroutine) and one method (the list).
package Local::List;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
$self->list([]);
}
sub list {
my $self = shift;
my $list_ref = shift;
if (ref $list_ref ne "ARRAY) {
return;
}
if (defined $list_ref) {
$self->{LIST} = $list_ref;
}
if wantarray {
return $self->{LIST};
}
}
The first thing: Use the same standard names everyone else uses. Use new for the constructor. When I try to look at the documentation on how to use your class, I can search for the word new and know that's how I create a class object. Also, use the variable names $class and $self. That's what everyone else does, so it's easy to know what's going on.
Notice in my new subroutine, the first item passed is the name of the class while the first item passed to my other subroutines is a reference to my class object (i.e. $self). That's probably the hardest thing to understand about classes.
Notice in new, I immediately create my $self and bless it. That way, I can call my other subroutines (my methods) to do the setting for me. This way, my constructor doesn't know how my class is structured. This has a lot of advantages:
When (not if) I modify my class, I don't have to modify the constructor.
My constructor is always in sync with all of my methods.
I don't have to know how my class object is structured when I start defining the class. I can start writing my class without worrying about all those dirty details on how it'll work.
Notice that the list subroutine (or method) can either set a list or return a list. It's much easier if you use the same subroutine to set or get the value. Also in your method subroutines, use a blank return when your method function returns an error. Otherwise, always return something. That makes it easy to test to see if a method failed or not.
Let's look at some of the other methods you probably want to have. Let's have all the four standard list functions:
push
pop
shift
unshift
Here's an example:
sub push {
my $self = shift;
my $member = shift;
if (not defined $member) {
return;
}
my $list_ref = $self->list;
my $return = push #{ $list_ref }, $member;
$self->list($list_ref);
return $return;
}
Wow, that's simple. Notice that the pop doesn't know what my class looks like. It used the list method to retrieve a list reference. Then it used the builtin push method to push a member onto the list. I save that return value, and that's what I'll return. I'm not even sure what push returns. All I know is that push returns something if it succeeds. (Yes, I know it returns the number of items in the list).
The other three functions are more or less the same. Here's a few more:
current
splice
next
previous
head
last
All you need to do for current is to store the current value. Use the same function to set and get the value. Notice that my list method or my push method, or my new constructor knows or care how you store it. Nor, do our next and previous methods. All they need to do is increment or decrement the value of current and store it back using the current method subroutine:
sub next {
my $self = shift
my #list = $self->list; #Returns a list;
my $current = $self->current;
my $list_size = $#list;
if ($current eq $list_size) {
return; #Can't return a value after the end of the list!
}
$current++; #Increment the value;
my $value = $list[$current]; #I'll return this
$self->current($current) #Store the new current
return $value;
}
And, now to the basis of your question: Getting the last and head values of the list. Here's last
sub last {
my $self = shift;
my $list_ref = $self->list;
return ${ $list_ref }[-1];
}
And a quick copy and paste will give me head:
sub head {
my $self = shift;
my $list_ref = $self->list;
return ${ $list_ref }[0];
}
That's it! All that worrying you were doing was for naught.
Sorry for the long post. I just wanted to emphasize that object oriented programming in Perl isn't that tricky as long as you follow a few simple guide lines.
(Simple? What about use Moose; No, I said simple!). ;-)
I just want to post my final working version for the record and for your feedback/comments.
Thanks again!!
package List;
my $head = undef;
my $last = undef;
sub new {
my ($class, $val) = #_;
my $self = {};
# init the head of the list
if (!defined $head) {
$head = $self;
print "updated the head of the list ($head)" . "\n";
}
else {
$last->{nextNode} = $self; # update previous node to point on this new one
}
$last = $self; # this object is now the last one
$self->{value} = $val; # store the value
$self->{nextNode} = undef; # reset next to nothing since this node is last
return bless $self, $class;
}
sub setVal {
my ($class, $val) = #_;
$class->{value} = $val;
}
sub getVal {
my $class = shift;
print $class->{value};
}
sub getNext {
my $class = shift;
return $class->{nextNode};
}
# return true if this is the last node, otherwise false.
sub isLast {
my $class = shift;
return 1 if !defined $class->{nextNode};
return 0;
}
sub getLast {
return $last;
}
sub getHead {
return $head;
}
# looping through all the list and printing the values
sub showList {
my $node = $head; # set temp node to the head
while ( !$node->isLast() ) {
print $node->{value} . "\n";
$node = $node->{nextNode};
}
# printing last value. (should be defined but I check it just in case)
print $node->{value} . " (last)\n" if defined $node->{value};
}
1;
Script:
my $n0 = new List(4);
my $n1 = new List(8);
my $n2 = new List(9);
my $n3 = new List(3);
my $n4 = new List(1);
my $n5 = new List(0);
my $n6 = new List(5);
print "\nShow list: \n";
$n2->showList(); # any object will print the list
Related
I am writing a simple Perl module that handles time and date information. Below is a simplified version of the constructor and one of the accessor methods:
package Time;
# Constructor
sub new {
my $class = shift;
my %args = #_;
# Create an empty object
my $self = bless {};
# Get input arguments
my $second = exists $args{'second'} ? $args{'second'} : (localtime)[0];
# Use the accessor to validate and populate object data
$self->second($second);
return $self;
}
# Second accessor
sub second {
my $self = shift;
my $second = shift;
# Input validation
if (defined $second) {
if ($second =~ m{^\d+$} && $second >= 0 && $second <= 59) {
$self->{'second'} = $second;
}
else {
die "'second' must be an integer between 0 and 59!";
}
}
return $self->{'second'};
}
My question is... Is it acceptable to use an accessor in object construction in Perl?
Using the accessor inside of the constructor makes it so I don't need all of the validation code in the constructor itself since the input is validated by the accessor, but is this bad practice?
Is there any reason I would not want to do this?
Yes, you can do that.
But from an OOP perspective it might make sense to move that code into an init method, so the constructor only creates the object. It's basically done after you've called bless.
sub new {
my $class = shift;
# Create an empty object
my $self = bless {};
# initialize the object
$self->init(#_);
return $self;
}
sub init {
my ($self, %args) = #_;
# Get input arguments
my $second = exists $args{'second'} ? $args{'second'} : (localtime)[0];
# Use the accessor to validate and populate object data
$self->second($second);
return;
}
Have you considered using Moose? You can also use Type::Tiny for your type constraints, even if you want to stick with manual oop.
I'm pretty new to perl, and I'm getting stuck on a homework problem. I have an object with a class variable that counts the number of instances created. Then I have a subclass with an instance variable.
My first question is, how do I make the class variable hidden from the user? I tried using closures but couldn't figure out how to make inheritance work with that. And the fact that it's a class variable made it worse because the code that increments it executed twice and it said I had two instances when I had one. Not exactly sure why it happened but it makes sense. I tried using scalars but the variable again wasn't incrementing correctly. Haven't tried "inside-out objects" yet and I'm not sure I want to, it seems way over my head. I'm getting the feeling that encapsulating class variables is different than encapsulating instance variables, but I can't find anything that explains how to do it.
My second questions is, as I mentioned, I can't get encapsulation to work with inheritance. With closures when you call the super constructor from the subclass you get a reference to the subroutine right, so there's no way (that I know of) to add the instance variables to that.
Here's my base class:
#!/usr/bin/perl -w
use strict;
package Base;
my $count = 1;
sub new {
my $class = shift;
my $self = {
_Count => $count # not hidden
};
$count++; # increment count
bless $self, $class;
return $self;
}
sub Count { # getter
my $self = shift;
return $self->{_Count};
}
1;
Here's my subclass:
#!/usr/bin/perl -w
use strict;
package Sub;
use Base;
our #ISA = qw(Base);
sub new {
my $class = shift;
my $self = $class->SUPER::New();
$self->{_Name} = undef; # not hidden
return $self;
}
sub Name { #getter/setter
my($self, $name) = #_;
$self->{_Name} = $name if defined($name);
return $self->{_Name};
}
1;
If you are using bare Perl 5 (rather than employing an OO framework), the usual way to do class variables is as a lexical visible only to the accessor:
{
my $count = 0;
sub Count {
my ($self, $new_count) = #_;
if (defined $new_count) { # NB only works if undef is not a legit value
$count = $new_count;
}
return $count;
}
}
$count is only visible in the enclosing block; not even other methods on the same class can see it. But anyone can manipulate it with either $base_obj->Count or Base->Count, and any such manipulation will affect the shared variable.
You can also employ closure to provide really-hidden instance variables. This is not worth doing unless you are fulfilling the arbitrary rules of a homework assignment.
package Base;
sub new {
my ($class, $name) = #_;
die "Need name!" unless defined $name;
my $age;
return bless sub {
my ($attribute, #args) = #_;
if ($attribute eq 'name') {
if (#args) {
die "Attempt to set read-only attribute!";
}
return $name;
}
if ($attribute eq 'age') {
if (#args) {
($age) = #args;
}
return $age;
}
die "Unknown attribute $attribute";
} => $class;
}
sub name {
my ($self, #args) = #_;
return $self->(name => #args);
}
sub age {
my ($self, #args) = #_;
return $self->(age => #args);
}
What happens here is that the blessed sub returned by new closes over two lexicals, $name and $age. When new returns, those lexicals go out of scope and the only way to access them from that point forward is through the closure. The closure can inspect its arguments to permit or deny access to the values it holds. So long as it never returns a reference, it can be sure that it has the only direct access to those variables.
This works with inheritance, too, without too much added subtlety:
package Derived;
use base 'Base';
sub new {
my ($class, $name, $color) = #_;
my $base_instance = $class->SUPER::new($name);
return bless sub {
my ($attribute, #args) = #_;
if ($attribute eq 'color') {
if (#args) {
($color) = #args;
}
return $color;
}
# base class handles anything we don't, possibly by dying
return $base_instance->($attribute, #args);
} => $class;
}
This emulates what languages with distinct storage for base- and derived-class instance data do, either handling the request locally or passing it on to the base class instance, which has been added to the closure. Deeper inheritance trees will result in closures that close over closures that close over closures, each of them optionally also closing over instance variables needed by that particular class.
This is a pretty big mess to produce and really hard to inspect and debug, which is why I'm going to emphasize one more time that you should never do this. But it is very useful to understand, to which end I refer you to SICP.
As a module-local my variable, $count is already hidden from users of the module/class. It appears as if you're using instance variable _Count as a "current ID" type variable, so that each object (instance) created gets a new ID starting from 1. (If instead it is meant to track the number of active instances, then you need to decrement it in DESTROY and there's no need to store a copy in the object.) If your test code is only creating one instance then its Count() method should return 1 but $count will be 2, since it started as 1 and was incremented after storing the old value in the object.
It is typical in perl to store instance variables in the $self hash as you are doing, without hiding them, although sometimes a prefix is used to avoid collisions. They are protected more by convention (it's not safe to rely on implementation details because they might change) than language features.
Take a look at the Moose suite of modules if you want higher-level control over perl classes.
To quote perldoc perlmodlib, "Perl does not enforce private and public parts of its modules as you may have been used to in other languages like C++, Ada, or Modula-17. Perl doesn't have an infatuation with enforced privacy. It would prefer that you stayed out of its living room because you weren't invited, not because it has a shotgun."
The standard convention in Perl is to put everything into the $self hash and use an underscore prefix to indicate which items should be treated as private... and then trust users of the class to respect that indication. The same convention is also applied to methods. If you use one of my modules and you choose to peek under the covers and modify the contents of $self directly or call $obj->_some_private_method, then you're going off into the woods and may break something, or what works fine in this version may break when you upgrade to the next version; if that happens, you get to keep both pieces.
If you're going to insist on making data inaccessible to anyone outside the class itself, there are ways to do that, but a) they add complexity which is, in almost all cases, unnecessary and b) as you've already seen, they have a tendency to make inheritance a lot more of a hassle to work with.
My question to you, then, is what are you actually attempting to accomplish and why do you feel the need to make your object data Sooper-Sekret and completely inaccessible? What benefit will you gain by doing so which isn't provided by simply marking things that you think should be treated as private, then trusting others to leave them alone (unless they have good reason to do otherwise)?
In Perl, fields are not usually hidden by enforcing this through the semantics of the language, but rather through a contract in the form of documentation. However, fields can be hidden through the use of closures. It is also worth noting that Perl does not semantically differentiate between class methods and instance methods.
One of the standard ways to implement objects is a blessed hash, like you do. This hash contains all instance variables / fields. It is customary to start "private" fields with an underscore. Usually, the contract (the documentation) will not state how these fields are stored, but will require the user of the class to go through various method calls.
Class variables should not be stored with the instance. It is better to use global variables, or lexical variables. In the code you gave, $count is just a counter, but you never access it as a class variable. Instead, you assign each instance an unique ID. To use it as a class variable, provide an appropriate accessor (I stripped out unneccessary stuff like returns):
{
package Base;
my $count = 0;
sub new {
my ($class) = #_;
my $self = {
ID => $count++,
};
bless $self, $class;
}
sub Count { $count }
sub ID { my ($self) = #_; $self->{ID} }
sub report { my ($self) = #_; "I am the Base object ".($self->ID)."." }
}
=head1 Base
A generic base class
=head2 Base->Count
Return the object count.
=head2 $base->ID
Give the unique ID of this object.
=head2 $base->report
Returns a string containing a short description.
=cut
The subclass has no business meddling with the count. This is enforced by the scope of the variable $count above, denoted via the outer curly braces. The subs are closures over this variable.
{
package Sub;
use parent -norequire, qw(Base); # remove `-norequire` if Base in different file
sub new {
my ($class) = #_;
my $self = $class->SUPER::new;
$self->{Name} = undef;
$self;
}
sub Name :lvalue {
my ($self) = #_;
$self->{Name};
}
sub report {
my ($self) = #_;
"I am the Sub object ".($self->ID)." called ".($self->Name).".";
}
}
=head1 Sub
A generic subclass. It subclasses Base.
=head2 $sub->Name [= SCALAR]
Gets or sets the name of $sub.
my $oldname = $sub->Name;
$sub->name = "new name";
=cut
As you can see, the Sub constructor calls the Base initializer, then adds a new field. It has no class methods or class variables. The class has no access to the $count variable, except via the accessor class method. The contract is stated via POD documentation.
(In the Name method, I use an :lvalue annotation. This allows me to simply assign to the appropriate field in the object. However, this disallows argument checking.)
The testcase
my $base1 = Base->new; my $base2 = Base->new;
print "There are now " . Base->Count . " Base objects\n";
my $sub1 = Sub->new; my $sub2 = Sub->new;
print "There are now " . Base->Count . " Base objects\n";
$sub2->Name = "Fred";
print $_->report . "\n" for ($base1, $sub1, $base2, $sub2);
prints
There are now 2 Base objects
There are now 4 Base objects
I am the Base object 0.
I am the Sub object 2 called .
I am the Base object 1.
I am the Sub object 3 called Fred.
Beautiful, isn't it? (Except $sub1, that object is missing its name.)
The documentation can be viewed with perldoc -F FILENAME, and would output something like
Base
A generic base class
Base->Count
Return the object count.
$base->ID
Give the unique ID of this object.
$base->report
Returns a string containing a short description.
Sub
A generic subclass. It subclasses Base.
$sub->Name [= SCALAR]
Gets or sets the name of $sub.
my $oldname = $sub->Name;
$sub->name = "new name";
only typeset more nicely, if you are on a *nix system.
Tested under v5.12.4.
Edit: Inside-out objects
While inside-out objects provide better encapulation, they are a bad idea: difficult to understand, difficult to debug, and difficult to inherit they provide more problems than solutions.
{
package Base;
my $count = 0;
sub new { bless \do{my $o = $count++}, shift }
sub Count { $count }
sub ID { ${+shift} }
sub report { my ($self) = #_; "I am the Base object ".($self->ID)."." }
}
{
package Sub;
my #_obj = ();
my $count = 0;
sub new {
my ($class) = #_;
$count++;
$_obj[$count - 1] = +{
parent => Base->new(),
Name => undef,
};
bless \do{my $o = $count - 1}, shift;
}
sub Name :lvalue { $_obj[${+shift}]{Name} }
sub AUTOLOAD {
my $self = shift;
my $package = __PACKAGE__ . "::";
(my $meth = $AUTOLOAD) =~ s/^$package//;
$_obj[$$self]{parent}->$meth(#_)
}
sub report {
my ($self) = #_;
"I am the Sub object ".($self->ID)." called ".($self->Name).".";
}
}
This implementation has the exact same interface, and completes the test case with the same output. This solution is far from optimal, supports only single inheritance, does some intermediate stuff (autoloading, dynamic method calls), but it does suprisingly work. Each object is actually just a reference to an ID that can be used to look up the actual hash containing the fields. The array holding the hashes is not accessible from the outside. The Base class has no fields, therefore no object array had to be created.
Edit2: Objects as coderefs
Yet another bad idea, but it is fun to code:
{
package Base;
my $count = 0;
sub new {
my ($class) = #_;
my $id = $count++;
bless sub {
my ($field) = #_;
die "Undefined field name" unless defined $field;
if ($field eq "ID") { return $id }
else { die "Unrecognised name $field" }
}, $class;
}
sub Count { $count }
sub ID { my ($self) = #_; $self->("ID") }
sub report { my ($self) = #_; "I am the Base object " . $self->ID . "." }
}
{
package Sub;
use parent -norequire, qw(Base);
sub new {
my ($class) = #_;
my $name = undef;
my $super = $class->SUPER::new;
bless sub {
my ($field, $val ) = #_;
die "Undefined field name" unless defined $field;
if ($field eq "Name") { defined $val ? $name = $val : $name }
else { $super->(#_) }
}, $class;
}
sub Name { my $self = shift; $self->("Name", #_) }
sub report {
my ($self) = #_;
"I am the Sub object ".($self->ID)." called ".($self->Name).".";
}
}
The test case has to be adapted to $sub2->Name("Fred"), and the documentation updated accordingly, as we cannot use an lvalue annotation here safely.
First, I'm not sure exactly what you mean by "hidden from the user", but it looks like you may be looking for package scoped variables (our) vs. instance scoped.
package MyBaseClass;
use warnings;
use strict;
our $counter = 0;
sub new {
my $class = shift;
$counter++;
return bless {}, $class;
}
sub howManyInstances {
return $counter;
}
1;
On your second question, I'm not sure what closures have to do with inheritance.
Here's a simple subclass:
package MySubClass;
use warnings;
use strict;
use parent 'MyBaseClass'; # use parent schema, don't mess with #ISA
sub new {
my $class = shift;
my $self = $class->SUPER::new(#_);
$self->{_name} = undef;
return $self;
}
# Your setter/getter looks ok as is, though lowercase is tradional for methods/subs
1;
Now, if this were real code you would not do it like this - you would use Moo or Moose.
How do you make one setter method, and one getter method to manage access to fields of an object? The new subroutine looks like this:
sub new {
my $class = shift;
my $self = {#_};
bless($self,$class); # turns this into an object
}
Creation of a new object looks like this:
$foo = Package::new("Package",
"bar", $currentBar,
"baz", $currentBaz,
);
This is not a good idea.
Perl instituted the use of use strict; to take care of problems like this:
$employee_name = "Bob";
print "The name of the employee is $employeeName\n";
Mistyped variable names were a common problem. Using use strict; forces you to declare your variable, so errors like this can be caught at compile time.
However, hash keys and hash references remove this protection. Thus:
my $employee[0] = {}
$employee[0]->{NAME} = "Bob";
print "The name of the employee is " . $employee[0]->{name} . "\n";
One of the reasons to use objects when you start talking about complex data structures is to prevent these types of errors:
my $employee = Employee->new;
$employee->name("Bob");
print "The name of the employee is " . $employee->Name . "\n";
This error will get caught because the method name is name and not Name.
Allowing users to create their own methods at random removes the very protection we get by using objects:
my $employee = Employee->new;
$employee->name("Bob"); #Automatic Setter/Getter
print "The name of the employee is " . $employee->Name . "\n"; #Automatic Setter/Getter
Now, because of automatic setters and getters, we fail to catch the error because any method the user names is valid -- even if that user made a mistake.
In fact, I setup my objects so my object doesn't necessarily know how it's structured. Observe the following class with methods foo and bar:
sub new {
my $class = shift;
my $foo = shift;
my $bar = shift;
my $self = {};
bless $self, $class;
$self->foo($foo);
$self->bar($bar);
return $self;
}
sub foo {
my $self = shift;
my $foo = shift;
my $method_key = "FOO_FOO_FOO_BARRU";
if (defined $foo) {
$self->{$method_key} = $foo;
}
return $self->{$method_key};
}
sub bar {
my $self = shift;
my $bar = shift;
my $method_key = "BAR_BAR_BAR_BANNEL";
if (defined $bar) {
$self->{$method_key} = $bar;
}
return $self->{$method_key};
}
I can set the class values for foo and bar in my constructor. However, my constructor doesn't know how those values are stored. It simply creates the object and passes it along to my getter/setter methods. Nor, do my two methods know how they store each other's value. That's why I can have such crazy names for my method's hash keys because that is only available in the method and no where else.
Instead, my methods foo and bar are both setters and getters. If I give them a value for foo or bar, that value is set. Otherwise, I simply return the current value.
However, I'm sure you already know all of this and will insist this must be done. Very well...
One way of handling what you want to do is to create an AUTOLOAD subroutine. The AUTOLOAD subroutine automatically is called when there's no other method subroutine by that name. The $AUTOLOAD contains the class and method called. You can use this to setup your own values.
Here's my test program. I use two methods bar and foo, but I could use any methods I like and it would still work fine
One change, I use a parameter hash in my constructor instead of a list of values. No real difference except this is considered the modern way, and I just want to be consistent with what everyone else does.
Also notice that I normalize my method names to all lowercase. That way $object->Foo, $object->foo, and $object-FOO are all the same method. This way, I at least eliminate capitalization errors.
use strict;
use warnings;
use feature qw(say);
use Data::Dumper;
my $object = Foo->new({ -bar => "BAR_BAR",
-foo => "FOO_FOO",
}
);
say "Foo: " . $object->foo;
say "Bar: " . $object->bar;
$object->bar("barfu");
say "Bar: " . $object->bar;
say Dumper $object;
package Foo;
sub new {
my $class = shift;
my $param_ref = shift;
my $self = {};
bless $self, $class;
foreach my $key (keys %{$param_ref}) {
# May or may not be a leading dash or dashes: Remove them
(my $method = $key) =~ s/^-+//;
$self->{$method} = $param_ref->{$key};
}
return $self;
}
sub AUTOLOAD {
my $self = shift;
my $value = shift;
our $AUTOLOAD;
( my $method = lc $AUTOLOAD ) =~ s/.*:://;
if ($value) {
$self->{$method} = $value;
}
return $self->{$method};
}
Something like this...
sub get {
my $self = shift;
my $field = shift;
return $self->{$field};
}
sub set {
my $self = shift;
my $field = shift;
$self->{$field} = shift;
}
...makes it possible to write
$obj->set(foo => 'my foo value');
print $obj->get('foo');
But nowadays, it is very common to just use Moose.
I've just started to learn about tie. I have a class named Link which I would like to do the following thing:
if fetched, return the link's address
if stored, store the new address
be able to call methods on it
So far, my code is :
package Link;
sub FETCH {
my $this = shift;
return $this->{"site"};
}
sub STORE {
my ($self,$site) = #_;
$self->{"site"} = $site;
}
sub print_method {
my $self = shift;
print $self->{"site"};
}
sub TIESCALAR {
my $class = shift;
my $link = shift;
my $this = {};
bless($this,$class);
$this->{"site"} = $link;
return $this;
}
1;
And the code I'm using to check the functionality is:
use Link;
tie my $var,"Link","http://somesite.com";
$var->print_method;
When ran, the script will terminate with the following error:
Can't call method "print_method" without a package or object reference at tietest.pl line 4..
If I understand its message correctly, $var->print_method resolves to some string upon which the method print_method is called. How could I benefit from tie, but also use the variable as an object?
EDIT: after experimenting a bit,I found out that if I return $self on fetch , I can call the methods , however , fetch won't return the address .
EDIT 2:the perl monks supplied me the solution : tied . tied will return a reference to the object VARIABLE .
By combining tied with my methods , I can accomplish everything I wanted .
Tie is the wrong tool for this job. You use ties when you want the same interface as normal data types but want to customize how the operations do their work. Since you want to access and store a string just like a scalar already does, tie doesn't do anything for you.
It looks like you want the URI module, or a subclass of it, and perhaps some overloading.
If you really need to do this, you need to use the right variable. The tie hooks up the variable you specify to the class you specify, but it's still a normal scalar (and not a reference). You have to use the object it returns if you want to call methods:
my $secret_object = tie my($normal_scalar), 'Tie::Class', #args;
$secret_object->print_method;
You can also get the secret object if you only have the tied scalar:
my $secret_object = tied $normal_scalar;
I have an entire chapter on tie in Mastering Perl.
I suggest making a normal Perl object and then overloading stringification. You lose the ability to store a value through assignment, but retain the ability to get the value out by printing the object. Once you start wanting to call methods directly, an object is probably what you want.
package Link;
use strict;
use Carp;
use overload
(
'""' => sub { shift->site },
fallback => 1,
);
sub new
{
my $class = shift;
my $self = bless {}, $class;
if(#_)
{
if(#_ == 1)
{
$self->{'site'} = shift;
}
else { croak "$class->new() expects a single URL argument" }
}
return $self;
}
sub site
{
my $self = shift;
$self->{'site'} = shift if(#_);
return $self->{'site'};
}
sub print_method
{
my $self = shift;
print $self->site, "\n";
}
1;
Example usage:
use Link;
my $link = Link->new('http://somesite.com');
print $link, "\n"; # http://somesite.com
$link->print_method; # http://somesite.com
If you really, really want assignment to work too, you can combine a normal object with overloaded stringification (Link, above) with tie:
package LinkTie;
use strict;
use Link;
sub FETCH
{
my $this = shift;
return $this->{'link'};
}
sub STORE
{
my($self, $site) = #_;
$self->{'link'}->site($site);
return $site;
}
# XXX: You could generalize this delegation with Class::Delegation or similar
sub print_method
{
my $self = shift;
print $self->{'link'}->print_method;
}
sub TIESCALAR
{
my $class = shift;
my $self = bless {}, $class;
$self->{'link'} = Link->new(#_);
return $self;
}
1;
Example usage:
tie my $link,'LinkTie','http://somesite.com';
print $link, "\n"; # http://somesite.com
$link->print_method; # http://somesite.com
$link = 'http://othersite.com';
print $link, "\n"; # http://othersite.com
$link->print_method; # http://othersite.com
This is all quite hideous and a long way to go just to get the dubious ability to assign to something that you can also call methods on and also print as-is. A standard URI object with stringification is probably a better bet.
I often find it useful to be able to schedule code to be executed upon leaving the current scope. In my previous life in TCL, a friend created a function we called defer.
It enabled code like:
set fp [open "x"]
defer("close $fp");
which was invoked when the current scope exited. The main benefit is that it's always invoked no matter how/where I leave scope.
So I implemented something similar in Perl but it seems there'd be an easier way. Comments critiques welcome.
The way I did it in Perl:
create a global, tied variable which holds an array of subs to be executed.
whenever I want to schedule a fn to be invoked on exit, I use local to change the array.
when I leave the current scope, Perl changes the global to the previous value
because the global is tied, I know when this value change happens and can invoke the subs in the list.
The actual code is below.
Is there a better way to do this? Seems this would be a commonly needed capability.
use strict;
package tiescalar;
sub TIESCALAR {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub FETCH {
my $self = shift;
return $self->{VAL};
}
sub STORE {
my $self = shift;
my $value = shift;
if (defined($self->{VAL}) && defined($value)) {
foreach my $s (#{$self->{VAL}}) { &$s; }
}
$self->{VAL} = $value;
}
1;
package main;
our $h;
tie($h, 'tiescalar');
$h = [];
printf "1\n";
printf "2\n";
sub main {
printf "3\n";
local $h = [sub{printf "9\n"}];
push(#$h, sub {printf "10\n";});
printf "4\n";
{
local $h = [sub {printf "8\n"; }];
mysub();
printf "7\n";
return;
}
}
sub mysub {
local $h = [sub {printf "6\n"; }];
print "5\n";
}
main();
printf "11\n";
Well, your specific case is already handled if you use lexical filehandles (as opposed to the old style bareword filehandles). For other cases, you could always use the DESTROY method of an object guaranteed to go to zero references when it goes out of scope:
#!/usr/bin/perl
use strict;
use warnings;
for my $i (1 .. 5) {
my $defer = Defer::Sub->new(sub { print "end\n" });
print "start\n$i\n";
}
package Defer::Sub;
use Carp;
sub new {
my $class = shift;
croak "$class requires a function to call\n" unless #_;
my $self = {
func => shift,
};
return bless $self, $class;
}
sub DESTROY {
my $self = shift;
$self->{func}();
}
ETA: I like brian's name better, Scope::OnExit is a much more descriptive name.
Instead of using tie for this, I think I'd just create an object. You can also avoid the local that way too.
{
my $defer = Scope::OnExit->new( #subs );
$defer->push( $other_sub ); # and pop, shift, etc
...
}
When the variable goes out of scope, you have a chance to do things in the DESTROY method.
Also, in the example you posted, you need to check that the values you store are code references, and it's probably a good idea to check that the VAL value is an array reference:
sub TIESCALAR { bless { VAL => [] }, $_[0] }
sub STORE {
my( $self, $value ) = #_;
carp "Can only store array references!" unless ref $value eq ref [];
foreach { #$value } {
carp "There should only be code refs in the array"
unless ref $_ eq ref sub {}
}
foreach ( #{ $self->{VAL}} ) { $_->() }
$self->{VAL} = $value;
}
You may want to try out B::Hooks::EndOfScope
I Believe this works:
use B::Hooks::EndOfScope;
sub foo {
on_scope_end {
$codehere;
};
$morecode
return 1; # scope end code executes.
}
foo();
I think you want something like Scope::Guard, but it can't be pushed. Hmmm.
Thanks.
Trivially,
sub OnLeavingScope::DESTROY { ${$_[0]}->() }
used like:
{
...
my $onleavingscope = bless \sub { ... }, 'OnLeavingScope';
my $onleavingscope2 = bless \\&whatever, 'OnLeavingScope';
...
}
(The extra level of having a reference to a reference to a sub is necessary only to work around an optimization (that's arguably a bug) when using a non-closure anonymous sub.)