CGI/Perl Guide | Learning Center | Forums | Advertise | Login Site Search: in Perl Guide PerlGuru Forums Learning Ctr

Home: Perl Programming Help: Beginner:
private and protected methods

ChopperCharles
Novice

Mar 28, 2012, 1:02 PM

Post #1 of 32 (9727 views)
 private and protected methods Can't Post
I have an easy way of making methods private:

 Code
package foobar;  my $m_Self = undef; my$m_LastError = "";  sub new {   my $type = @_;$m_Self = {};   bless $m_Self,$type;   return $m_Self; } sub __foo { #private if ($_[0] eq $m_Self) {$m_LastError = "Access to private method denied.";     return -1;   }   my $name = shift(@_); print "Hello$name.\n";   return 0; }  sub run {#public, but can also be called from this package.   if ($_[0] eq$self) { shift(@_); }   my $name = shift(@_); return __foo($name); }  sub iAmFoobar { #public   return run("foobar"); } 1;

This works great, but now I want to make protected methods. Is there a way I can do this easily? I want to turn __foo into a protected method so that I can use it from a subclass... the only way I've been able to figure out how to call it is like this:

 Code
package TacoBell; our @ISA = qw(foobar);  my $m_Self = undef; sub new { my ($type) = @_;         $m_Self =$type->SUPER::new();     bless $m_Self,$type;     return $m_Self; } sub burrito { return$m_Self->SUPER::__foo("Charles"); } 1;

This doesn't work, for some reason when I call $m_Self->SUPER::__foo("Charles"),$self is added to the parameter list. I've tried this:

 Code
   if ($_[0] eq$self) {      my $me = shift; if ($me->isa(__PACKAGE__) ){       print("is a package.\n");     } else { print "not a package.\n"; }

but "is a package" always prints, regardless of whether I call $tacobell->burrito() or$tacobell->__foo("Charles");.

How do I go about doing this?

Thanks.

Charles.

(This post was edited by ChopperCharles on Mar 28, 2012, 1:04 PM)

budman
User

Mar 28, 2012, 5:09 PM

Post #2 of 32 (9718 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post

I changed the way __foo and run handles its arg checking.

 Code
 package foobar;  use Carp;   sub new {    my $class = shift; my$self = {};    bless ($self,$class);   return $self; } sub __foo { #private my$name = shift;   if ( ref($name) ) { my$self = $name; # make it private if ( ref($self) ne __PACKAGE__ ) {           confess("Access to private method denied.");       }       $name = shift; } print "Hello$name.\n";   return 0;  }    sub run {#public, but can also be called from this package.    my $name = shift; if ( ref($name) ) {      my $self =$name;      $name = shift; return$self->__foo($name); } else { return __foo($name);   } }    sub iAmFoobar { #public    return run("foobar");  }  1;

To inherit, you need to use the parent.
You can do:
use foobar;
use base foobar;

Or look into Moose.pm:
extends 'foobar';

Edit: when you "use parent" it does a require on the module and pushes the module onto @ISA. No need to explicitly push @ISA.

 Code
package TacoBell;  use parent foobar;              sub new {      my $class = shift; my$self = $class->SUPER::new(); #my$self = {};     #bless ($self,$class);     return $self; } sub burrito { my$self = shift;     return $self->__foo("Charles"); # calling the inherited __foo from parent will fail } sub burrito2 { my$self = shift;     # use this form if you need the super class's method      # to run before your overrides take place     my $x =$self->SUPER::__foo("Charles");     # override some stuff     return $x; } 1; Tests  Code perl -MTacoBell -le '$t=TacoBell->new();$t->iAmFoobar' Hello foobar. perl -MTacoBell -le '$t=TacoBell->new();$t->__foo("Chuck")' Access to private method denied. at foobar.pm line 17. foobar::__foo('TacoBell=HASH(0x605998)', 'Chuck') called at -e line 1 perl -Mfoobar -le '$t=foobar->new();$t->iAmFoobar' Hello foobar. perl -Mfoobar -le '$t=foobar->new();$t->__foo("Chuck")' Hello Chuck. perl -MTacoBell -le '$t=TacoBell->new();$t->burrito()' Access to private method denied. at foobar.pm line 15. foobar::__foo('TacoBell=HASH(0x605998)', 'Charles') called at TacoBell.pm line 17 TacoBell::burrito('TacoBell=HASH(0x605998)') called at -e line 1 perl -MTacoBell -e '$t=TacoBell->new();$t->burrito2()' Access to private method denied. at foobar.pm line 15. foobar::__foo('TacoBell=HASH(0x605a70)', 'Charles') called at TacoBell.pm line 26 TacoBell::burrito2('TacoBell=HASH(0x605a70)') called at -e line 1 Hope that helps (This post was edited by budman on Mar 29, 2012, 10:00 AM) ChopperCharles Novice Mar 29, 2012, 8:12 AM Post #3 of 32 (9688 views)  Re: [budman] private and protected methods [In reply to] Can't Post I think I've got the concept, but I'm running into some down-right weirdness in my sample code. I keep getting "Can't locate object method iAmfooBar via package 1 at test1.pl line 15. It works from one class but not the other. The actual files I'm using are attached. note "base" and "parent" gave me errors, so I did not use them. Charles. Attachments: fooBar.pm (0.86 KB) TacoBell.pm (0.35 KB) test1.pl (0.47 KB) ChopperCharles Novice Mar 29, 2012, 8:40 AM Post #4 of 32 (9686 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post Okay, right after I posted that I realized the problem was in foobar.pm, my$type = @_; should be my ($type) = @_; That being said, the sample code doesn't work.$ret_code = $fb->__foo("d'oh"); should return an error, but it does not. Instead it prints the hash. Charles. budman User Mar 29, 2012, 9:47 AM Post #5 of 32 (9681 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post Test your return values in test1.pl Success is 0, Fail is -1 - you were checking ret_code == 0  Code $ret_code = $t->__foo("Charles"); print "RC:$ret_code\n"; if ($ret_code != 0) { print "t->__foo(\"Charles\") failed.\n"; } Correct the " my$type = @_ " was actually getting the number of args and then using that number to bless the hash with. Changing it to " my ($type) = @_; " was correct. That should work for you now. What was the error for base and parent? Did you use it as follows:  Code use parent 'fooBar'; What version of perl are you using? (This post was edited by budman on Mar 29, 2012, 9:54 AM) ChopperCharles Novice Mar 29, 2012, 12:22 PM Post #6 of 32 (9667 views)  Re: [budman] private and protected methods [In reply to] Can't Post Nope, my error codes are correct. inside of test1.pl, calling __foo from ANY object should return nonzero (ie, it shouldn't work). If I get a return code of 0, that means it's not working properly. I should ONLY be able to call __foo() from inside TacoBell.pm or fooBar.pm. Nowhere else. test1.pl doesn't inherit from fooBar.pm, so it should not be able to call __foo(). In short, if the program prints those "failed" messages to the console, it didn't work. It appears that I am using perl 5.1. (I cannot change this) Charles. ChopperCharles Novice Mar 29, 2012, 12:30 PM Post #7 of 32 (9666 views)  Re: [budman] private and protected methods [In reply to] Can't Post Another question... how do I reference the private member variables I created in fooBar from TacoBell? fooBar.pm has a member variable called$m_LastError, but I cannot find a way to set it from TacoBell.pm without first creating a setter method.

Charles.

budman
User

Mar 30, 2012, 7:12 AM

Post #8 of 32 (9627 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post
Let me clarify, the return code that you are getting in test1.pl is -1.
That is correct, however, the value you are testing for failure is 0, which is success. Your if statement is incorrect.

I understand what you want to do. The previous __foo I showed would only lock it down to the parent. To allow only the parent and any of its children to access it, you would need to check if the object isa fooBar.

 Code
sub __foo { #private    my $name = shift; if (ref($name)) {     my $self =$name;     unless ( (caller)[0]->isa( ref($self) ) ) {$m_LastError = "Access to private method denied.";       return -1;     }     $name = shift; } print "Hello$name.\n";    return 0;  }

Also, in test1.pl checking the retcodes needs to be fixed.

 Code
my $ret_code = 0; print "TacoBell:\n"; my$t = TacoBell->new(); $ret_code =$t->iAmfooBar(); if ($ret_code) { print "t->iAmfooBar() failed.\n"; }$ret_code = $t->__foo("Charles"); if ($ret_code) { print "t->__foo(\"Charles\") failed.\n"; }  print "fooBar:\n"; my $fb = fooBar->new();$ret_code = $fb->iAmfooBar(); if ($ret_code) { print "f->iAmfooBar() failed.\n"; } $ret_code =$fb->__foo("d'oh"); if ($ret_code) { print "f->__foo(\"d'oh\") failed.\n"; }  Code Output: TacoBell: Hello fooBar. t->__foo("Charles") failed. fooBar: Hello fooBar. f->__foo("d'oh") failed. This shows that IAmfooBar is running the private __foo which is correct. The fails occur when test1.pl tries to access them directly. Other than this, I don't know of another way to enforce private subs. Maybe using a closure in fooBar.pm. Not sure. Perl doesn't enforce private, everything is public. Private is done by convention, meaning its upto the programmer to KEEP it legit. Usually, when methods begin with underscores, it's a message to other programmers that this really should be treated as private and ignore it. Otherwise, there is no guarantee it will not break your app in the future. You may want to look over the OO perldoc, they are really worth the time. perlboot Perl OO tutorial for beginners perltoot Perl OO tutorial, part 1 perltooc Perl OO tutorial, part 2 perlbot Perl OO tricks and examples Otherwise, you may need to look into using Moose or other Class modules. Perl 5.1 is quite old. I would ask to update to at least 5.12 if possible. (This post was edited by budman on Mar 30, 2012, 7:22 AM) budman User Mar 30, 2012, 7:58 AM Post #9 of 32 (9623 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post The variables$m_Self and $m_LastError would be known as class attributes / variables. Using 'my' allows fooBar to hide these values from all other subclasses. In other words, private. In order to share these with the subclasses, you need to use 'our' (no longer need to 'use vars', our does this now).  Code in fooBar.pm: our$m_LastError;  in TacoBell.pm:   print $fooBar::m_LastError; Variables are not inherited. Also, this becomes a maintenance nightmare. This also breaks the rules of OOP, you are now treating it like a module. You are getting into data storage now. Again, you should read the perl docs. They cover all of this. If you want to keep them as class attributes, then you need to add accessors so the subclasses can access them.  Code in fooBar: sub getLastError { return$m_LastError; }  in TacoBell:   print $self->getLastError(),"\n"; This is the preferred method, because you are encapsulating the data, subclasses really shouldn't care about how its stored. Also, you are making your code very maintainable. Future programmers will love you. There has been a recent change in creating accessors, instead of making a get and set for each attrib, we are now making dual purpose accessor/setor.  Code sub lastError { my$self = shift;     if (@_) {           $self->{"_LAST_ERROR"} = shift; } return$self->{"_LAST_ERROR"}; }  in TacoBell:    print $self->lastError(); # get it$self->lastError("something happened");   # set it

Try to stay away from using class variables/attributes if you can.
You really don't need $m_Self. If objects are blessed, they automatically pass theirs$self as the first arg.

The Error message would be better tied to the object not the class.
I usually limit class attributes to set defaults for the class.
Everything else is in the object. Also, for object vars in the class, I like to use uppercase and a leading underscore as well. Note, if you are using 'use strict', you must quote the hash key if it has a leading underscore.
ex: $self->{"_VAR"} or$self->{'_VAR'}

Lastly, using the class attribute for error message has a bug.
This may look like its working fine now. But.. add a second instance of TacoBell and you now have a very hard to track bug.

I changed fooBar.pm as follows:

 Code
sub __foo { #private    my $name = shift; if (ref($name)) {     my $self =$name;     unless ( (caller)[0]->isa( ref($self) ) ) {$self->lastError("Access to private method denied. $_[0]");$m_LastError = "Access to private method denied. $_[0]"; return -1; }$name = shift;     if ( $name =~ /Burr/ ) {$self->lastError("Weeeee Burrito!");     }   }   print "Hello $name.\n"; return 0; } # here is you class attribute accessor sub GetLastError { return$m_LastError; }  # using the object data sub lastError {     my $self = shift; if (@_) {$self->{"_LAST_ERROR"} = shift;     }     return $self->{"_LAST_ERROR"}; } I added a second instance to test1.pl  Code print "TacoBell:\n"; my$t = TacoBell->new(); $ret_code =$t->iAmfooBar(); if ($ret_code) { print "t->iAmfooBar() failed.\n"; }$ret_code = $t->__foo("Charles"); if ($ret_code) { print "t->__foo(\"Charles\") failed.\n"; } $ret_code =$t->burrito(); if ($ret_code) { print "t->burrito() failed.\n"; } print$t->GetLastError();   print "\nTacoBell2:\n"; my $tb = TacoBell->new();$ret_code = $tb->iAmfooBar(); if ($ret_code) { print "tb->iAmfooBar() failed.\n"; } $ret_code =$tb->__foo("Peter"); if ($ret_code) { print "tb->__foo(\"Peter\") failed.\n"; } print$t->GetLastError(),"\n"; print $t->lastError(),"\n"; print$tb->GetLastError(),"\n"; print $tb->lastError(),"\n"; Output:  Code TacoBell2: Hello fooBar. tb->__foo("Peter") failed. Access to private method denied. Peter <--- WRONG Weeeee Burrito! Access to private method denied. Peter Access to private method denied. Peter The error code for the first instance was overwritten (one object bled into another). The class attribute exists for ALL instances. Remember, when dealing with objects, just because you used 'my' doesn't mean its limited to just that instance. (This post was edited by budman on Mar 30, 2012, 8:27 AM) ChopperCharles Novice Mar 30, 2012, 8:16 AM Post #10 of 32 (9620 views)  Re: [budman] private and protected methods [In reply to] Can't Post Okay, I've changed the code to clarify the ret_codes to make them less confusing. At any rate, the code is attached, however, the output is incorrect. When I run perl test1.pl, I should receive: Hello fooBar. Hello fooBar. Instead, I get: Hello fooBar. Hello fooBar. Hello fooBar=HASH(0x8ca8818) Assertion failed. Charles. (This post was edited by ChopperCharles on Mar 30, 2012, 8:17 AM) Attachments: test1.pl (0.58 KB) fooBar.pm (0.86 KB) TacoBell.pm (0.35 KB) ChopperCharles Novice Mar 30, 2012, 8:23 AM Post #11 of 32 (9618 views)  Re: [budman] private and protected methods [In reply to] Can't Post okay cool, switching to your isa() code seems to be working. I have tried several samples of isa I found on google, but couldn't get any of them to do what I wanted. Thanks! Charles. budman User Mar 30, 2012, 8:37 AM Post #12 of 32 (9616 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post I looked over the scripts you sent. I tried the test1.pl with the assertions, and it ran as expected Hello fooBar. Hello fooBar. Note: I updated my previous reply to show a bug with your$m_LastError attribute. If you switch it to use the lastError setter/accessor style you should be fine. The problem occurs when you add more than one instance of TacoBell.

Edit: I would really ditch $m_Self. Its another bug waiting to happen. It too is prone to multiple instances issue. Currently you only use it in new(), but anywhere else would cause you to access incorrect objects, as the reference is rewritten with each TacoBell->new() that is created. (This post was edited by budman on Mar 30, 2012, 8:46 AM) ChopperCharles Novice Mar 30, 2012, 8:45 AM Post #13 of 32 (9614 views)  Re: [budman] private and protected methods [In reply to] Can't Post Maybe it has to do with different versions of perl, but that didn't work for me until I changed it to use the isa code you sent. I don't understand the problem with lasterror, what are you saying? Charles. budman User Mar 30, 2012, 9:02 AM Post #14 of 32 (9613 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post The two vars in fooBar.pm,  Code  my$m_Self;  my $m_LastError; are bugs waiting to happen. If you create more than one instance of TacoBell, you now have multiple objects overwriting global class variables. This is very bad. Bug 1: anytime TacoBell->new() is run to create a new instance, the global class variable$m_Self is ovewritten with the new object ref. If this variable $m_Self is used anywhere else in fooBar.pm (except in 'new') you will have objects calling other objects or worse other objects updating other objects data! BAD BUG! Bug 2:$m_LastError is another global class variable. When ANY instance has an error, it is stored in the class. This means when you try to access the last error from instance #2, you are actually seeing the last instance that had an error - this could be instance #10, or #3, etc. Instance #2 loses it's last error message as soon as another error occurs anywhere. Thats a BAD BUG.

I know this is an example script, but these are very serious bugs that could lead to hours of debugging when its a real large production application. It's better to learn it now than later.

To fix those future bugs you would need to do the following:
1. eliminate $m_Self, and keep$self a lexical to new
2. use the object for data storage

 Code
sub new {   my ($type) = @_; my$self = {};    bless($self,$type);      return $self; } sub lastError { my$self = shift;     if (@_) {         $self->{"_LAST_ERROR"} = shift; } return$self->{"_LAST_ERROR"}; }

Does that help?

budman
User

Mar 30, 2012, 9:05 AM

Post #15 of 32 (9612 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post
Correct, the isa method is what you want to use. :)

ChopperCharles
Novice

Mar 30, 2012, 9:25 AM

Post #16 of 32 (9609 views)
 Re: [budman] private and protected methods [In reply to] Can't Post
So.... those are the equivalent of static variables in C++/java then? I don't want global variables, I want class member variables.

public class fooBar {
private String m_LastError = "";
};

is what I want.

ChopperCharles
Novice

Mar 30, 2012, 12:59 PM

Post #17 of 32 (9604 views)
 Re: [budman] private and protected methods [In reply to] Can't Post
The problem with your solution to moving $m_LastError into$m_Self is that if I call a method inside the pm, there is no $self passed. in foobar.pm, when the run method calls __foo, it doesn't pass a$self along with it.

This sounds like a huge can of worms, why would anyone design a language in this manner?

Charles.

budman
User

Mar 30, 2012, 2:05 PM

Post #18 of 32 (9602 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post
Perl and Python != Java and C++

Python does recognize some things like private functions when the name starts with an underscore. However, it is not FULLY protected. Perl leaves private vars and methods to convention.

To make a method work for either a passed ref, or a normal call,
check if the first arg is a ref.

 Code
sub run {    my $this = shift; if ( ref($this) ) { return $this->__foo(+shift) } else { return __foo($this) }    }

You can apply the same to __foo - check the first arg for ref,
and then proceed.

 Code
sub __foo {    my $this = shift; if ( ref($this) ) {       unless (  (caller)[0]->isa( ref($this) ) ) {$this->lastError("Access to private method denied.");          return -1;       }    }    my $name = ref($this) ? shift : $this; print "Hello$name\n";    return 0;    }

Perl was never designed to be OO, it was band-aided to work with objects. Since objects are pretty much containers, this worked fine for most OOP concepts. Just like in C++, classes are pretty much structs with their inners kept private, unless you specify otherwise.

(This post was edited by budman on Mar 30, 2012, 2:22 PM)

budman
User

Mar 30, 2012, 2:15 PM

Post #19 of 32 (9600 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post
Static in the sense that the class is only loaded once.

Each instance does not create a new copy of the parent. So in a sense, these variables become static, but they are global to the class and subclasses via the accessors/setters.

If you wanted to track the number of instances loaded, you would use a class attribute, and increment it in new. Then create a sub DESTROY to decrement the attribute when objects are released.

Again, I think you should read the perldocs for OO and check out some comparisons, what you can and cannot do with Perl OOP. There are a lot of good examples in the perldocs that will answer a lot the questions you have and will have as you progress.

If you are looking for a good book on Perl OO - get "Object Oriented Perl" by D. Conway. He explains what each OO concept is and how it applies to Perl.

(This post was edited by budman on Mar 30, 2012, 2:17 PM)

ChopperCharles
Novice

Mar 30, 2012, 2:52 PM

Post #20 of 32 (9595 views)
 Re: [budman] private and protected methods [In reply to] Can't Post
Okay, I'm getting it now. There's another problem with the way that works though, in that if I don't have a $self passed in to a method, I cannot access properties of that class except through a function setter/getter. No direct access to$self->{dataItem}, even I have a getDataItem subroutine, bcause getDataItem is going to need a $self to get at that data. So in essence, I have to completely change the class I've been writing to work correctly... ah well, I learned something at least. (Err, not the foobar test classes I've been talking about, but rather the real code I'm working on :) I'm assuming I'm not going to have any problems storing arrays or hashes in$self? (I know, big assumption)

Charles.

(This post was edited by ChopperCharles on Mar 30, 2012, 2:54 PM)

budman
User

Mar 30, 2012, 5:09 PM

Post #21 of 32 (9585 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post

I think your on the right track.

Storing data in hashes is quite easy. What's happens in the class, should stay in the class. Especially when it comes to data. This way in the future you can make changes to the base class, and not have to worry about fixing subclasses as long as you keep the accessors the same.

If you need to freeze and thaw the object, for faster load times, look into the storable module to help with that.

I played around with Moose to show how it would look (similar to Perl 6).

 Code
	package fooBar;         use Modern::Perl; 	use Moose;          my $m_LastError = ''; after 'new' => sub { say "created instance from ".__PACKAGE__ }; has iAmFooBar => ( isa => 'Str', is => 'ro', lazy => 1, default => sub { run("fooBar") }, ); has lastError => ( isa => 'Str', is => 'rw', default => '', ); around lastError => sub { my$orig = shift;             my $self = shift; return blessed$self ? $self->$orig(@_) : $m_LastError ; }; has priv_attrib => ( isa => 'Int', is => 'ro', writer => 'private_set_attrib', default => sub { shift->default_value } ); has run => (is => 'rw'); around 'run' => sub { my$orig = shift;             my $self = shift; return blessed$self ? $self->__foo(@_) : __foo($self,@_);          };           sub isa_friend {             my $class = shift; my$this = shift;             if ( blessed $this ) { unless ($class->isa( ref($this) ) ) {$this->lastError("Access to private method denied.");                   return 0;                }             }             return 1;         }          sub default_value { return 0 }          	sub __foo { 	   my $this = shift; # private access only my$class = (caller)[0]; 	   return -1 unless isa_friend( $class,$this); 	   my $name = blessed$this ? shift : $this; print "Hello$name\n"; 	   my $error_occurred = 0; # to test modular code if ( ! blessed$this && $error_occurred != 0 ) {$m_LastError = "some error"; 	       return $error_occurred; } else {$m_LastError = "" } 	   return 0;    	}   	1;

 Code
	package TacoBell;         use Modern::Perl; 	use Moose; 	extends 'fooBar'; 	 	after 'new' => sub { say "we can add stuff in ".__PACKAGE__ }; 	 	has burrito => (            is  => 'ro',            isa => 'Str',            default => sub {                my $self = shift; my$retcode = $self->__foo("Burrito!");$self->lastError("problem with burrito") if $retcode; return$retcode;             },             lazy => 1,             );                  	1;

 Code
#!/usr/bin/perl use Modern::Perl; use TacoBell; my $tb = TacoBell->new(); say "private var is ".$tb->priv_attrib(); $tb->private_set_attrib(55); say "private var is now ".$tb->priv_attrib();  # cause read-only error #$tb->priv_attrib(10); say "\ntb->Burrito:"; my$retcode = $tb->burrito(); say "RC:$retcode ", $tb->lastError(); say "\ntb->iAmFooBar:";$retcode = $tb->iAmFooBar(); say "RC:$retcode ", $tb->lastError(); say "\ntb->__foo test";$retcode = $tb->__foo("test"); say "RC:$retcode ", $tb->lastError(); say "\nFooBar:"; my$fb = fooBar->new();  say "\nfb->iAmFooBar:"; $retcode =$fb->iAmFooBar(); say "RC: $retcode ",$fb->lastError();  say "\nfb->__foo test"; $retcode =$fb->__foo("test"); say "RC: $retcode ",$fb->lastError();  # don't do this :) #say "\nModular test for __foo test"; #$retcode = fooBar::__foo("test"); #say "RC:$retcode ", fooBar::lastError();

output:

 Code
created instance from fooBar we can add stuff in TacoBell private var is 0 private var is now 55  tb->Burrito: Hello Burrito! RC: 0   tb->iAmFooBar: Hello fooBar RC: 0   tb->__foo test RC: -1 Access to private method denied.  FooBar: created instance from fooBar  fb->iAmFooBar: Hello fooBar RC: 0   fb->__foo test RC: -1 Access to private method denied.

(This post was edited by budman on Mar 30, 2012, 5:15 PM)

budman
User

Mar 30, 2012, 5:50 PM

Post #22 of 32 (9581 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post

Anytime we need access to the object, we make sure to call the method with $self-> this way Perl will pass the first arg for us. This is because$self was blessed. If you do use Data::Dumper; then run print Dumper($self) you will see how the hash is blessed.$self->run("something");
$self->__foo(@_); Now we have access to any of the object vars. The$self->run( ) is the same as run($self, @_); (try not to do this) (This post was edited by budman on Mar 30, 2012, 5:52 PM) ChopperCharles Novice Apr 2, 2012, 11:47 AM Post #23 of 32 (9482 views)  Re: [budman] private and protected methods [In reply to] Can't Post Okay, I've changed my code around, but there's still a problem: unless ( (caller)[0]->isa( ref($self) ) ) {
$self->__SetLastError("Access to protected method denied."); return -1; } This doesn't work when the caller is a subclass. If I have class B that inherits from class A,$self will be B, but caller will be A.

Charles.

ChopperCharles
Novice

Apr 2, 2012, 3:24 PM

Post #24 of 32 (9467 views)
 Re: [budman] private and protected methods [In reply to] Can't Post
Another problem.... I'm using Class::Struct in my class. I used to have an array of these structs stored in my @m_StructArray, and accessed with a getter:

sub getStructArray { return @m_StructArray; }

I could easily access the array from outside the package,like this:

 Code
my @strArray = obj->getStructArray(); print $strArray[0]->data . "\n"; Now I'm trying to store them in$self{_StructArray} instead, and no matter what I do I cannot seem to be able to access it in the same way. What is the syntax for storing and retrieving an array from inside $self? I've tried:  Code sub getStructArray{ my$self = shift;    return $self->{_StructArray}; } sub getStructArray{ my$self = shift;   return @{$self->{_StructArray}}; } sub getStructArray{ my$self = shift;    my @tmp= $self->{_StructArray}; return @tmp; } sub getStructArray{ my$self = shift;    my @tmp= @{$self->{_StructArray}}; return @tmp; } and maybe a few more. I'm just shooting in the dark trying different syntax and hoping something works, but so far all I've managed is a litany of unhelpful error messages. There's something simple I'm missing, but I've spent the better half of the day searching google (and those sites you mentioned), and haven't found anything helpful. Charles. wickedxter User Apr 2, 2012, 6:20 PM Post #25 of 32 (9462 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post use datadumper module and dump the object ref. to find out its internals.. (This post was edited by wickedxter on Apr 2, 2012, 6:21 PM) ChopperCharles Novice Apr 3, 2012, 7:57 AM Post #26 of 32 (2794 views)  Re: [wickedxter] private and protected methods [In reply to] Can't Post Actually, I tried that last night and that helped some, but for some reason I'm getting undef as the first entry in my array when it's stored in$self.

So, inside a hash I'm guessing that since I can add anything at any time, regardless of the fact I'm using strict... that means somehow when I try and clear the array I'm adding an extra undef in?

How do I correctly clear and update an existing entry in the hash?

Charles.

budman
User

Apr 3, 2012, 8:18 PM

Post #27 of 32 (2791 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post

You would need to store the values in an anonymous array

 Code
push @{  $self->{"_StructArray"} },$element;  push @{  $self->{"_StructArray"} }, @array; push @{$self->{"_StructArray"} },  ( $e1,$e2, @array);  # for array of hashes push @{  $self->{"_StructArray"} }, { a=>1, b=>2 }; # or just a hash$self->{"_StructArray"}{a} = 1; $self->{"_StructArray"}{b} = 2; To access them:  Code use Data::Dumper; print Dumper($self->{"_StructArray"} );  print  $self->{"_StructArray"}[0]; foreach my$i ( @{  $self->{"_StructArray"} } ) { print "$i\n"; }  # for array of hashes foreach my $i ( @{$self->{"_StructArray"}  } ) {    foreach my $k (sort keys %{$i}) {        print  "$k$i->{$k}\n"; } } Note: When key names start with an underscore, you should quote the key name as it may lead to issues with strict pragma. I took a look at Class::Struct. My quick guess is that it takes each key you add in the struct method and auto-generates a setter and getter.  Code $element_value = $obj->s; # element value$obj->s('new value');

In order for your object to work like this, you would need to create the setter and getters for each key. This can be done by the AUTOLOAD to create the getter/setter on the fly. Otherwise, it's pretty monotonous. For what it's worth, to save time, headaches, and use proven well tested code - use Moose.

 Code
sub s {     my ($self) = shift; if (@_) {$self->{'_Struct'}{s} = shift }     return $self->{'_Struct'}{s}; } sub t { my ($self) = shift;     if (@_) { $self->{'_Struct'}{t} = shift } return$self->{'_Struct'}{t}; }

These can be pretty repetitive and you notice a pattern.
This is the idea behind Class::Struct, you can use AUTOLOAD, or better maybe to build it at compile time using BEGIN.

The attributes should be unique, or you can override existing subs.

 Code
# handle undefined subs package tester; BEGIN {     my @attribs = qw(s t u);     for my $attrib ( @attribs ) { no strict 'refs'; # add getter/setter method *{"$attrib"} = sub {              my $self = shift; if (@_) {$self->{'_Struct'}{$attrib} = shift }; return$self->{'_Struct'}{$attrib}; }; } } sub new { my$class = shift;    my $self = {};$self->{'_Struct'} = {};    bless ($self,$class);    return $self; } 1; #!/usr/bin/perl use strict; use warnings; my$k = tester->new(); $k->s(1);$k->t(2); $k->u(3); printf "s = %s\nt = %s\nu = %s\n",$k->s, $k->t,$k->u;

Output:
s = 1
t = 2
u = 3

(This post was edited by budman on Apr 3, 2012, 8:20 PM)

budman
User

Apr 3, 2012, 9:00 PM

Post #28 of 32 (2788 views)
 Re: [ChopperCharles] private and protected methods [In reply to] Can't Post

 Quote
Okay, I've changed my code around, but there's still a problem:

unless ( (caller)[0]->isa( ref($self) ) ) {$self->__SetLastError("Access to protected method denied.");
return -1;
}

This doesn't work when the caller is a subclass. If I have class B that inherits from class A, $self will be B, but caller will be A. Since the subclass is inheriting from the parent, all the methods belong to the subclass, except those methods that are overridden. To access the parents versions, you must use SUPER. Even calling SUPER, as the TacoBell new method does, it still reports the caller as TacoBell. When I ran a test on the script provided, I see that is the case: # all the taco bell objects NEW -> Self: TacoBell Caller: TacoBell t = TacoBell->new(); Self: Caller: fooBar __foo -> t->iAmfooBar() success. Self: TacoBell Caller: main __foo -> t->__foo("Charles") failed. Self: TacoBell Caller: TacoBell (this is in the base class) __foo -> t->burrito() success. # the foobar objects NEW -> Self: fooBar Caller: main f = fooBar->new(); Self: fooBar Caller: main __foo -> f->__foo("d'oh") failed. Again, this is all in the perldoc object oriented tutorials. (This post was edited by budman on Apr 3, 2012, 9:03 PM) ChopperCharles Novice Apr 4, 2012, 8:38 AM Post #29 of 32 (2772 views)  Re: [budman] private and protected methods [In reply to] Can't Post So basically in order for inheritance and protected/private methods to work in a way that is not spaghetti left and right, the base class needs to know the name of all subclasses? That's hardly OO. As for the other issue with the undef, I figured it out. Thanks. I have been reading the OO for perl pages, btw. Charles budman User Apr 4, 2012, 9:13 PM Post #30 of 32 (2758 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post I don't think I follow on the base class needing to know its subclasses. When TacoBell inherits from foobar, TacoBell extends foobar but its identity is still TacoBell. When you issue, use parent fooBar, this uses fooBar to load its methods into TacoBell's namespace/package, and then pushes fooBar onto @ISA. @ISA is used as a method lookup table, that sets a lookup precedence for method calling. When a method is requested, it searches TacoBell, in case of any overrides, and then fooBar. Perl doesn't really support "Private" anything. Its all Public. We are using a hack to simulate Private functions. Python has the same issue, but decided to remove any methods from its lookup tables that begin with an underscore. However, this can be bypassed very easily, because even though it may appear to be private, its still public. In Perl and Python, private is done by convention only. It's up to the programmer to respect this. Now in Perl 6, there has been requests to add 'private' which will do exactly as you expect. So this may find it's way into Perl 5 as well. As for Moose, there is an extension that does private and protected modes. But, they are still remain public. http://lumberjaph.net/perl/2009/06/30/private-and-protected-methods-with-moose.html Also, there are other ways, using trait and writer attribs in Moose to simulate private. http://stackoverflow.com/questions/3996503/how-can-i-create-internal-private-moose-object-variables-attributes (This post was edited by budman on Apr 4, 2012, 9:16 PM) ChopperCharles Novice Apr 6, 2012, 12:17 PM Post #31 of 32 (2745 views)  Re: [budman] private and protected methods [In reply to] Can't Post Inside a method in fooBar:  Code unless ( (caller)[0]->isa( ref($self) ) ) {...}
$self is fooBar, but caller is TacoBell. Even though caller inherits from fooBar, this will not work. So, inside fooBar I need:  Code unless(index(caller[0], "fooBar") != -1 && index(caller[0], "TacoBell") != -1)) eg, if caller is fooBar or caller is tacoBell, we're good. I don't want to rely on calling conventions. People often mess with things they shouldn't -- The goal here is to make it that much more difficult for someone to do something stupid. If I could, I'd abandon perl altogether and write it in java or c++, but I'm extending an open source project and I am limited with what resources I can use. I can't even use a later version of perl. budman User Apr 7, 2012, 7:56 AM Post #32 of 32 (2736 views)  Re: [ChopperCharles] private and protected methods [In reply to] Can't Post So you do not want this to be inherited or used by anything else.  Code # create a class attribute my @myFriends = ( __PACKAGE__, "TacoBell" ); # change the unless statement in __foo to: ( my$object = (caller)[0] ) =~ s/.*:://; unless ( grep { $object } @myFriends ) {$m_LastError = "Access to private method denied. \$object";     return -1; }

Strange though, @ISA is pretty safe with inheritance.

Experienced Perl programmers have a lot of respect for each others logic and implementations. They know the limitations of the language and respect it and make it work. Just because objects can be accessed, or jail-breaked, or whatever, a majority if not all try to follow the best practice guidelines.

(This post was edited by budman on Apr 7, 2012, 8:05 AM)

 Announcements     PerlGuru Announcements Perl Programming Help     Frequently Asked Questions     Beginner     Intermediate     Advanced     Regular Expressions     mod_perl     DBI     Win32 Programming Help Fun With Perl     Perl Quizzes - Learn Perl the Fun Way     Perl Golf     Perl Poetry Need a Custom or Prewritten Perl Program?     I need a program that...     I Need a Programmer for Freelance Work     Throw Down The Gauntlet General Discussions     General Questions     Feedback     Tutorial/Article Suggestions for The Learning Cent     Internet Security Other Programming Languages     Javascript     PHP

 Search this forum this category all forums for All words Any words Whole Phrase (options) Powered by Gossamer Forum v.1.2.0