Private methods within a Perl Object module

J

Johnny J

I'm doing object oriented programming w/ Perl and created a Perl module
for a class. What I am trying to do within the module itself is
implement private methods or subroutines that can only be used by
(possibly public) methods within the module; I do not want them
accessible outside the module.

Here's how I'm trying to do it in the module. For flexibility, I wanted
some of my public methods to invoke the private subroutines via a hash
table to subroutine refs (which are the private subs). For what I am
trying to do, this scheme adds much flexibility and manageability, as
opposed to calling the private subroutines explicitly in the methods by
name.
-------------------------------------------------
my %PRIVMETHS = ( 'MOVE' => $prv_MOVE,
'WRITE' => $prv_WRITE,
'READ' => $prv_READ,
'DELETE' => $prv_DELETE, );

##### Private Subroutines (not access. to public) ########
my $prv_MOVE = sub {
....
}

my $prv_WRITE = sub {
....
}

my $prv_READ = sub {
....
}

my $prv_DELETE = sub {
....
}

##### Public Methods for Object Class ########
sub action {
my $self = shift;
# Method called as $obj->action(<action>, <value>), where <action> is
# any one of MOVE, WRITE, READ, DELETE, ...

...
$PRIVMETHS{$action}->($value);
...
}
-------------------------------------------------

Unfortunately, I have not been able to get this to work. I have tried
so many other convolutions to get this to work, even using evals, but no
go. Any help would be greatly appreciated!

--john
 
A

Anno Siegel

Johnny J said:
I'm doing object oriented programming w/ Perl and created a Perl module
for a class. What I am trying to do within the module itself is
implement private methods or subroutines that can only be used by
(possibly public) methods within the module; I do not want them
accessible outside the module.

Here's how I'm trying to do it in the module. For flexibility, I wanted
some of my public methods to invoke the private subroutines via a hash
table to subroutine refs (which are the private subs). For what I am
trying to do, this scheme adds much flexibility and manageability, as
opposed to calling the private subroutines explicitly in the methods by
name.

I'm not sure I understand entirely what you want to achieve, but there's
enough wrong with your code to comment on.
-------------------------------------------------
my %PRIVMETHS = ( 'MOVE' => $prv_MOVE,
'WRITE' => $prv_WRITE,
'READ' => $prv_READ,
'DELETE' => $prv_DELETE, );

You are using the package global variables $prv_MOVE etc to set up the
hash. These are undefined at this point, as far as anyone can see, so
that's what's going into the hash.

You seem to be running without strict, otherwise your program would
have died at this point.

Quite generally, this kind of setup, which is going to be used by other
subs, should go into a BEGIN block, so it is ready when the subs are
ready. It may often not matter, but sometimes it does.
##### Private Subroutines (not access. to public) ########
my $prv_MOVE = sub {
....
}

Here you are setting the lexical variables $prv_MOVE etc. These are
different from the package globals you used above. But even if you
were using the same set of variables, what makes you think setting them
here would retro-actively change the values in your hash? Programs work
sequentially.
my $prv_WRITE = sub {
....
}

my $prv_READ = sub {
....
}

my $prv_DELETE = sub {
....
}

##### Public Methods for Object Class ########
sub action {
my $self = shift;
# Method called as $obj->action(<action>, <value>), where <action> is
# any one of MOVE, WRITE, READ, DELETE, ...

...
$PRIVMETHS{$action}->($value);
...
}
-------------------------------------------------

Unfortunately, I have not been able to get this to work. I have tried
so many other convolutions to get this to work, even using evals, but no
go. Any help would be greatly appreciated!

Well, the call should work if $action contains the right value. You just
never assigned anything useful to $PRIVMETHS{ $action}.

Judging from your attempt, you need to learn some elementary Perl, and
probably some programming concepts too, before you try to write your own
OO method dispatcher.

Anno
 
R

Randal L. Schwartz

Johnny> I'm doing object oriented programming w/ Perl and created a Perl
Johnny> module for a class. What I am trying to do within the module itself
Johnny> is implement private methods or subroutines that can only be used by
Johnny> (possibly public) methods within the module; I do not want them
Johnny> accessible outside the module.

The convention for this is to begin them with an underscore.

If you want more control than that, you know where to find C++ or Java.

Every time someone tries to restrict what can be done by subclasses
and foreign classes, someone else finds a legitimate reason to need
to work around that.

Please don't be that short-sighted.

And please supply an email address for people to contact you privately
if they choose.

print "Just another Perl hacker,"
 
U

Uri Guttman

AS> Quite generally, this kind of setup, which is going to be used by other
AS> subs, should go into a BEGIN block, so it is ready when the subs are
AS> ready. It may often not matter, but sometimes it does.

this doesn't need any BEGIN block and the real problem and fix is below:

why do you need the variable there just to insert it into a hash?

my %method_dispatch ;

$method_dispatch{ 'WRITE' } = sub {} ;

that should work now but still has a major bug. google for some threads
on diapatch tables and you should see the problem. this has been covered
many times and is now in the process of being added to the FAQ (i owe
an edit pass on the entry someone wrote).

AS> Judging from your attempt, you need to learn some elementary Perl,
AS> and probably some programming concepts too, before you try to
AS> write your own OO method dispatcher.

he wasn't that far off but i agree with your general sentiment. first, i
would never do this as it gains little. why shouldn't code that uses
this module be able to call those methods and instead be forced to call
them through this table. there is no noticeable benefit that i can see.

uri
 
B

Ben Morrow

Uri Guttman said:
he wasn't that far off but i agree with your general sentiment. first, i
would never do this as it gains little. why shouldn't code that uses
this module be able to call those methods and instead be forced to call
them through this table. there is no noticeable benefit that i can see.

The OP's stated intent was that these methods should be private: i.e.
not callable from outside the class... to make this work, I would ditch
the action() method and instead use AUTOLOAD:

use Carp;

our $AUTOLOAD;

sub AUTOLOAD {
(my $meth = $AUTOLOAD) =~ s/.*:://;
__PACKAGE__ eq caller and exists $dispatch{$meth}
or croak "Undefined subroutine &$AUTOLOAD called";
goto &$dispatch{$meth};
}

Ben
 
A

Anno Siegel

Uri Guttman said:
AS> Quite generally, this kind of setup, which is going to be used by other
AS> subs, should go into a BEGIN block, so it is ready when the subs are
AS> ready. It may often not matter, but sometimes it does.

this doesn't need any BEGIN block and the real problem and fix is below:

No, it doesn't *need* BEGIN (as far as we know the code), and I pointed
out as much. However, I've been bitten often enough by routines that
need initialization when trying to call such a beast too early (often
for debugging purposes). I like it when a routine is runnable as soon
as it is compiled.

[...]

Anno
 
J

Jay Tilton

:
: > he wasn't that far off but i agree with your general sentiment. first, i
: > would never do this as it gains little. why shouldn't code that uses
: > this module be able to call those methods and instead be forced to call
: > them through this table. there is no noticeable benefit that i can see.
:
: The OP's stated intent was that these methods should be private: i.e.
: not callable from outside the class...

Actually, he said "module," not "class," which suggested to me that he
wants the method accessible only within the file's lexical scope. This
deserves some clarification from the OP.

: to make this work, I would ditch
: the action() method and instead use AUTOLOAD:
:
: use Carp;
:
: our $AUTOLOAD;
:
: sub AUTOLOAD {
: (my $meth = $AUTOLOAD) =~ s/.*:://;
: __PACKAGE__ eq caller and exists $dispatch{$meth}
: or croak "Undefined subroutine &$AUTOLOAD called";
: goto &$dispatch{$meth};

Should be:

goto &{ $dispatch{$meth} };

: }

If he does want the method to be private to a lexical scope, that technique
is easily subverted by changing into the right package, e.g.

my $obj = MyClass->new;
{
package MyClass;
$obj->some_private_method;
}
 
U

Uri Guttman

BM> The OP's stated intent was that these methods should be private: i.e.
BM> not callable from outside the class... to make this work, I would ditch
BM> the action() method and instead use AUTOLOAD:

BM> use Carp;

BM> our $AUTOLOAD;

BM> sub AUTOLOAD {
BM> (my $meth = $AUTOLOAD) =~ s/.*:://;
BM> __PACKAGE__ eq caller and exists $dispatch{$meth}
BM> or croak "Undefined subroutine &$AUTOLOAD called";
BM> goto &$dispatch{$meth};
BM> }

that would work and be slower. my example of how to properly initialize
the dispatch table would work too and be faster. and i left the one bug
as an exercise to the OP. now there can a need for private methods and
perl6 will support them. i just want to hear the OP justify why it is
necessary in this case.

uri
 
U

Uri Guttman

JT> : our $AUTOLOAD;
JT> :
JT> : sub AUTOLOAD {
JT> : (my $meth = $AUTOLOAD) =~ s/.*:://;
JT> : __PACKAGE__ eq caller and exists $dispatch{$meth}
JT> : or croak "Undefined subroutine &$AUTOLOAD called";
JT> : goto &$dispatch{$meth};

JT> Should be:

JT> goto &{ $dispatch{$meth} };

JT> : }

JT> If he does want the method to be private to a lexical scope, that
JT> technique is easily subverted by changing into the right package,
JT> e.g.

JT> my $obj = MyClass->new;
JT> {
JT> package MyClass;
JT> $obj->some_private_method;
JT> }

good catch. i would just use a lexical dispatch table that was assigned
sub refs directly. no muss, no fuss. but the OP is silent. i will wait
to hear back from him.

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,141
Messages
2,570,817
Members
47,367
Latest member
mahdiharooniir

Latest Threads

Top