Beefy Boxes and Bandwidth Generously Provided by pair Networks Joe
Perl-Sensitive Sunglasses
 
PerlMonks  

How to use Inheritance with Inside-Out Classes and Hash Based Classes

by ghenry (Vicar)
on Nov 15, 2005 at 17:06 UTC ( #508694=perlquestion: print w/ replies, xml ) Need Help??
ghenry has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

I am playing with Inside-Out Classes and trying to do Inheritance, but not sure how to use hash-references within Objects of a Inside-Out class. An example class like so:

package Samba::LDAP; use version; $VERSION = qv('0.0.1'); use warnings; use strict; use Carp; use Class::Std::Utils; use File::Basename; use base qw(File::Samba); { # Attributes my %config_of; # Constructor takes the path of the Samba config file sub new { my ($class, $samba) = @_; # Bless a scalar to instantiate the new object... my $new_object = bless anon_scalar(), $class; # Initialise the object's "config" attribute... $config_of{ident $new_object} = $samba; return $new_object; } sub get_path { my ($self) = @_; return $config_of{ident $self}; } sub get_filename { my ($self) = @_; my $filename = basename($config_of{ident $self}); return $filename; } } 1;
And the client code I've written is like so:
use strict; use warnings; use Samba::LDAP; my $smbconf = Samba::LDAP->new(q{/etc/samba/smb.conf}); # path is loaded my $path = $smbconf->get_path(); print "$path\n"; my $filename = $smbconf->get_filename(); print "$filename\n"; my @shares = $smbconf->listShares(); print "@shares\n";

No you can see my problem, listShares(); is part of File::Samba, which is hash based. My program output is:

[ghenry@perlbox lib]$ perl ../scripts/test.pl /etc/samba/smb.conf smb.conf Not a HASH reference at /usr/lib/perl5/site_perl/5.8.6/File/Samba.pm l +ine 181.
So the listShares method of File::Samba expects the object to be a hash reference. How can I provide this?

Thanks and feel free to give me a slap if this is something trivial! ;-)

Gavin.

Walking the road to enlightenment... I found a penguin and a camel on the way.....
Fancy a yourname@perl.me.uk? Just ask!!!

2005-11-16 Retitled by planetscape, as per Monastery guidelines
Original title: 'How to use Inheritance with Inside-Out Classes and Hash Bassed Classes'

Comment on How to use Inheritance with Inside-Out Classes and Hash Based Classes
Select or Download Code
Re: How to use Inheritance with Inside-Out Classes and Hash Based Classes
by ikegami (Pope) on Nov 15, 2005 at 17:13 UTC

    The short answer is that you can't. You need to dispatch the methods to the "base class" yourself instead of letting perl do it through @ISA, because you need to use ident on the invoker first. In other words, you need to wrap the "base class" instead of inheriting from it.

    It can be done by removing the use base and adding one of the following functions:

    sub AUTOLOAD { my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::')+2); my $code_ref = File::Samba->can($method); if (!$code_ref) { require Carp; Carp::croak("Undefined subroutine $method called"); } # Create stub functions to avoid calling # AUTOLOAD more than necessary. my $stub = sub { # This only works with methods, # and only if they are non-static. local $_[0] = ident $_[0]; &$code_ref; }; { no strict 'refs'; *$method = $stub; } goto($stub); }

    or

    sub AUTOLOAD { my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::')+2); my $code_ref = File::Samba->can($method); if (!$code_ref) { require Carp; Carp::croak("Undefined subroutine $method called"); } # Don't create stub functions to avoid # clearing perl's method cache repeatedly. # See http://www.perlmonks.org/?node_id=505443 # This only works with methods, # and only if they are non-static. local $_[0] = ident $_[0]; goto($code_ref); }

    You might want to override can method to check File::Samba in addition to your class. The following should do the trick, but it's untested:

    sub can { my $p = shift(@_); return UNVERSAL::can($p, @_) || File::Samba->can(@_); }

    The above function have been tested (IIRC), but to a limited extent.

    There may be a better method.

    Update: Various bits added for roundness.

      This is also called delegation. Its generally preferred to ISA anyway since it doesn't specify that a particular object *is* another kind of thing, it has-a the other object, uses it, and forwards appropriate things through.
Re: How to use Inheritance with Inside-Out Classes and Hash Based Classes
by blazar (Canon) on Nov 15, 2005 at 17:16 UTC
    I am playing with Inside-Out Classes and trying to do Inheritance, but not sure how to use hash-references within Objects of a Inside-Out class.
    I think you may find this node by Abigail-II useful in these respects.
Re: How to use Inheritance with Inside-Out Classes and Hash Based Classes
by dragonchild (Archbishop) on Nov 15, 2005 at 17:23 UTC
    You just hit the problem with inheritance - you're extending the class, which means you're tied to its implementation details.

    You might want to look at delegation. Basically, you would contain a File::Samba object and you forward any method you don't override to it. Take a look at Tree::Compat for an example.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      You just hit the problem with inheritance - you're extending the class, which means you're tied to its implementation details.
      With traditional, hash-based objects, you are right. And that's true for most of the implementations based on a Class::* module of CPAN, including Class::Std as well. And even the new inside out based class being promoted here on Perlmonks last week.

      But the pure, not on a module depending, inside-out technique deals with inheritance perfectly. It can inherit from any implementation - and if inherited from, the inheritee can use whatever implementation it wants as well. Of course, if A inherits from B, B inherits from C, and B is using an inside-out technique, but A and C don't, C might still dictate how A is implemented.

      #!/usr/bin/perl use strict; use warnings; package HashBased; sub new { my $class = shift; return bless {@_}, $class; } sub noise { my $self = shift; printf "The %s goes %s!\n", $self->{name}, $self->{sound} } sub name { my $self = shift; return $self->{name}; } package InsideOut; { use Scalar::Util 'refaddr'; my %legs; our @ISA = 'HashBased'; sub new { my $class = shift; my $legs = pop; my $obj = bless $class->SUPER::new(@_), $class; $legs{refaddr $obj} = $legs; return $obj; } sub show_legs { my $self = shift; printf "The %s has %d legs\n", $self->name, $legs{refaddr $sel +f}; } sub DESTROY { my $self = shift; delete $legs{$self} } } package main; my $dog = InsideOut->new(name => 'dog', sound => 'bark', 4); my $bird = InsideOut->new(name => 'bird', sound => 'peep', 2); $dog->noise; $dog->show_legs; $bird->noise; $bird->show_legs; __END__ The dog goes bark! The dog has 4 legs The bird goes peep! The bird has 2 legs
      As you can see, the super class is using hash-based objects. But that doesn't prevent an inside-out based class to inherit from it. And you could even inherit from that class and use a hash-based implementation if you want to do so.
      Perl --((8:>*
Re: How to use Inheritance with Inside-Out Classes and Hash Based Classes
by Aristotle (Chancellor) on Nov 15, 2005 at 19:12 UTC

    I have no idea what everyone is talking about. Inside-out objects where invented precisely to make it possible to inherit without knowing any superclass implementation details. The sole trick to that is to let your superclass set up the blessed reference for you, then rebless it into your own package. After all, you don’t even care what the reference actually contains, all you need is an address.

    sub new { my ( $class, $samba ) = @_; my $self = $class->SUPER::new( $samba ); $config_of{ ident $self } = $samba; return bless $self, $class; }

    How else can File::Samba work, anyway, if you don’t let it initialise its own data structures by calling its constructor?

    Update: you don’t actually need to rebless the reference (unless your superclass is misbehaved).

    sub new { my ( $class, $samba ) = @_; my $self = $class->SUPER::new( $samba ); $config_of{ ident $self } = $samba; return $class; }

    Makeshifts last the longest.

      Thanks Aristotle, that's exactly what I was after.

      In fact, my actual question should have been phrased so, "How can I initialise File::Samba's data structures in the constructor of my Samba::LDAP class?"

      Thanks again,
      Gavin.

      Walking the road to enlightenment... I found a penguin and a camel on the way.....
      Fancy a yourname@perl.me.uk? Just ask!!!
        How can I initialise File::Samba's data structures in the constructor of my Samba::LDAP class?
        The simplest answer is: don't write a constructor in your Samba::LDAP class. Just inherit the constructor, and let it do what it does. If you need to initialize something in your Samba::LDAP class, write a sub "init". Then you can write:
        my $obj = Samba::LDAP->new(args needed by File::Samba)->init(initializ +ation arguments)
        But if you insist in doing multiple things (creation and initialization) in one sub, do something like:
        sub create_and_initialize { # 'new' is a bad name; it does more. my $class = shift; my $arg = pop; # Initialization argument for my class; my $obj = bless $class->SUPER::new(@_), $class; # Initialize the object using $arg. return $obj; }
        Perl --((8:>*

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://508694]
Approved by dragonchild
Front-paged by dragonchild
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (14)
As of 2014-04-17 18:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (453 votes), past polls