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:

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

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.

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.

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.

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.

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.

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.

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.

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.

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

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?

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.

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.

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)

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).

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.

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: