perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I know about @ISA(class1 class2), so I'm not asking about that, but how to merge them seamlessly into 1 derived class.

My base class uses an array of multiple objects of same type and that's not a problem. To mix and use some shorthand:

package File_Entry { (has 3 routines, constructor & accessors): sub new([name,[rating]]); filename([name]) rating([rating]) } package File_Entries { use fields qw(__entries);
This is an array of "File_Entry's"; I consider this to be my "Base Class" even though it uses "File_Entry", it doesn't have an "ISA" relation. It supports 3 public methods & 1 "friend"ly method:
our $pckg_vars = __PACKAGE__; sub new () { my $class=$_[0]; my $s = {}; # base class starts/nothing $s->{$pckg_vars} = {}; bless $s, ref($class) || $class; $s->_entries_init; return $s; } sub ratings ([index,[rating]) #r/w accessor sub filenames([index]) #r/o accessor sub _add_entry(\$) #adds entry of type File_Entry
Other classes derive mostly hierarchically:
package Class2; our $pckg_vars = __PACKAGE__; sub new(){ my $class=$_[0]; my $s = "Base"->new(params).... my $s->{$pckg_vars} = {}; #'local' class var space bless $s,$class; return $s; ...} package Class3; our $pckg_vars => __PACKAGE__; sub new(){ my $class=shift; my $s = Class2->new(params)... my $s->{$pckg_vars} = {}; #class3 local vars bless $s,$class; return $s; ....} <etc>
At some point, I have a class with multiple inheritance....
{ package Entry_Display_In_Wins; use strict; our @ISA = qw(Entry_Ordering GUI_Glue); use base qw(Entry_Ordering); # and GUI_Glue, but base deficient use fields qw( __width __height __timer__ticked __timer__en +abled); ... but then I get to "new" sub new($) ... my ($class,$args) = @_; my $s= PreviousClass->new($args); confess "Fatal: memory for object $pckg_vars " . "busy (in use by someone else).\n" if (defined $s->{$pckg_vars}); $s->{$pckg_vars} = {}; bless $s, $class; ... $s->{$pckg_vars}->{'__GUI_Glue'}= GUI_Glue->new(...); return $s;
My problem is when I have 2 classes I am deriving from, I also have two "new" statements -- both with "pointers" or "handles" to objects of different types.

While I can rely on perl's inheritance mechanism to search my "ISA" for methods, calling anything under "Gui_Glue" needs the "handle" associated with GUI_Glue -- not the handle I got back from "PreviousClass";

So while I can rely on further derived classes to have access to both parents (via ISA), methods in either parent will only work with the specific "handle" I got back from their "new" constructors.

It's like I need a merge function: $s=class1->new() || class2->new()

Forgive my cluelessness, but it seems that the package using multiple inheritance must manually redirect all calls *through itself*, to the right methods. So if an upper method wants "create_window" (from 2nd base class GUI_Glue), then the MIUC (Multiple-Inheritance-Using-Class) needs to have it's own "create_window', then call its parent with the appropriately stored "handle" to the 2nd base class

Is there some way to "merge" the two base classes or does Entry_Display_In_Wins need to catch calls to, and function as a "Case" or "Switch" statement for each call to the 2nd-base class? Did I explain this clearly enough? Thanks! Linda

Replies are listed 'Best First'.
Re: How to do multiple inheritance?
by GrandFather (Saint) on Sep 17, 2007 at 23:48 UTC

    The bottom line is that the derived class has to disambiguate access to base class members as required. Consider:

    package Foo; my $pkg = __PACKAGE__; sub new { my $class = shift; return bless {$pkg => {basetype => $pkg}}, $class; } sub showBase { my $self = shift; print ref ($self) . ": $self->{$pkg}{basetype}\n"; } package Bar; my $pkg = __PACKAGE__; sub new { my $class = shift; return bless {$pkg => {basetype => $pkg}}, $class; } sub showBase { my $self = shift; print ref ($self) . ": $self->{$pkg}{basetype}\n"; } package Baz; use base qw(Foo Bar); sub new { my $class = shift; my $bar = Bar::new ($class); my $foo = Foo::new ($class); my %self; @self{keys %$foo} = values %$foo; @self{keys %$bar} = values %$bar; return bless \%self, $class; } package Boo; use base qw(Foo Bar); sub new { my $class = shift; my $bar = Bar::new ($class); my $foo = Foo::new ($class); my %self; @self{keys %$foo} = values %$foo; @self{keys %$bar} = values %$bar; return bless \%self, $class; } sub showBase { my $self = shift; Foo::showBase ($self); Bar::showBase ($self); } package main; my $baz = Baz->new (); my $boo = Boo->new (); print "Baz's showBase:\n"; $baz->showBase (); print "Boo's showBase:\n"; $boo->showBase ();

    Prints:

    Baz's showBase: Baz: Foo Boo's showBase: Boo: Foo Boo: Bar

    DWIM is Perl's answer to Gödel
      GrandFather:
      The bottom line is that the derived class has to disambiguate access to base class members as required.
      ----
      *Ding*....that's not the answer I wanted to hear! :-)

      Not sure how it would be "implemented" internally, but my "SuperDeluxe" preference would be to explain to perl that "$s" (handle to left parent) should be used for left-parent accesses, and "$w" (handle to right parent) should be used for right-parent accesses.

      Something on the order of:

      @ISA=qw(Sclass, Wclass); .... new(){ my $s=Sclass->new(); TELL_ISA_LOOKUP_USING(Sclass, 'self') #(self=this class's handle) # and use the "wclass_handle_field as handle for Wclass: TELL_ISA_LOOKUP_USING(Wclass, 'self'->{'wclass_handle_field'}) .... }
      "Seems" like any class other than the 1st parent is a bit like a "second class citizen": can't specify in "use base(...)", and inheritance is only handled automatically for the left-most ISA member. Any other use of multiple inheritance, and it seems like it's mostly "do it yourself". Can't even have "structs" that are nested because of a disallowing of any "inherited" structs (as in "use struct...") -- was going to use that module (actually was using), until I got to next 'struct' further up....then I realized what I wanted to do wasn't supported...*sigh*.

      Thanks much -- I was just hoping I didn't understand something, since it seems like manual disambiguation and making sure correct handles are used with correct ancestor really is _not_ "multiple inheritance" -- since how can "MI" ever work "automatically", if non-primary base classes are always passed the handle to the primary base class? :-(

Re: How to do multiple inheritance?
by snopal (Pilgrim) on Sep 17, 2007 at 21:22 UTC

    My solution would to be to put a new wrapper in the second class and then invoke my constructor using that method. There is nothing holy about 'new'. It is just a method that is expected to perform as a constructor.

    Update: In re-reading your request, it appears that you are implying that you might be overlapping method names more than just 'new'. Obviously, the inheritance model will look at the left-most class first. If you need derived classes to use a method that refers to a method of the same name as one found in class one, perhaps the wrapper needs to be in the class joining the two (or more) base classes, redirecting to the appropriate base method.

      Understand what you are saying about overlapping names -- that's not really my _current_ issue (though that has come up a few times during development as I create "Objects" that act as layers upon earlier objects (like "ratings" in lower, base classes just is an accessor, but updating "ratings" in a "higher" (more derived) class will also trigger some other actions (like the DB layer has a flag that gets marked "dirty").

      No..it's more like... well (please note...I'm still in process of converting to classes, so they may not be "optimally" thought out).

      I wanted to "objectify" the GUI routines so it would be easier, later to swap to another GUI, but the GUI isn't referenced until the upper level display routines -- but some of the GUI routines need to reference / refer to the keyboard-event handler and the kybd-ev-handler needs to not only make possible changes in the GUI state, but also in the lower-level data structure -- like calling the "DB" file-io level (in case of keyboard input to "save", for example).

      So "part" of my problem is that the objects are not strictly hierarchical, but need to call up & down the "object chain".

      The other part -- lets say if "main" calls and wants to access a GUI routine to add user name to frame on "main_win_handle". So main needs to know that the "handle" (not sure I like that term, but is descriptive...it's a Blessed pointer but I wouldn't call it just a pointer, but it *seems* (I may have the wrong impression, but it seems...) that the "general order of business" (business meaning deriving objects from more base objects which themselves are derived, etc...), given my readings that some attention is given to being careful not-to-create derived objects with the same field names as base objects (**unless you deliberately want that**). There seems to be some minor amount of education aimed at watching out for overlapping names and some suggested methods for avoiding collisions: example -- one method suggested prefixing public members with the package name so same-name vars from different level packages won't accidentally step on each other.

      FWIW: I'm just using 'new' because it is customary .. not because I think it special, just like people often use "$self" to refer to the "blessed pointer-handle thingy" (:-)) that's passed into a method call (where I, instead, gravitate toward "$s").

      The general structures, _often_ seem to use an anonymous assoc. array to store object data -- so I'm seeing progs organized like:

      { package Mostest_Basest_Class; sub new(){ my $self = {}; $self->{'__datum0'}=(...); $self->{'__datum1'}=(...); $self->{'__datum2'}=(...); bless $self, (Mostest_Basest_Class); return $self; } # this is the "handle" I referred to # i.e. a blessed pointer to data&methods (the +object). # (stating what appears to be "the obvious" :- +)) #accessors sub datum0 (){} # return or set datum0 sub datum1 (){} # return or set datum1 sub datum2 (){} # return or set datum2 } #end package M_B_C { package Derived1; our @ISA=qw(Mostest_Basest_Class); sub constructor1(){ my $self = Mostest_Basest_Class->new([params]) $self->{'__Derived1_datum1'} = (...); $self->{'__Derived1_datum2'} = (...); return bless(ed) $self.... } sub datum1($x){..SUPER::datum1($x)...} # overrides base class but + calls its method sub Derived1_datum2(...){...} } { package Derived2; our @ISA=qw(Derived1); sub new{ my $s=Derived1->constructor1(...) ($s->{'__Derived2_datum1'}, $s-{'_Derived2_datum2'}) = (... , ...) +; return bless $self,...; } sub Derived2_datum1(){} #no overlap sub datum2(){ ...;SUPER::datum2();...} #override calls "Mostest_Basest's but ca +lls it } { package Derived3; our @ISA=qw(Derived2); sub new{ my $s=Derived2->new(...) # previous class ($s->{'__Derived3_datum1'}, $s->{'_Derived3_datum2'}) = (... , ... +); return bless $self, ...} sub Derived3_datum1(){}... sub Derived3_datum2(){}... } # and "so on" where higher classes call constructor of lower class ============== but then ==== { package Base2_GUI; sub new(){ my $s = {}; $s->{'__datum_A'}=(...); $s->{'__datum_B'}= (...); return bless $s, $class; return $self; } sub GUI_datum_A (){} sub GUI_datum_B (){} for $sub in qw(init_GUI make_window refresh_window relabel_window) { #use GUI's "make_sub" to create other subs make_sub($sub); } } # now Derived4 uses both Derived3 AND Base2... { package Derived4; our @ISA=qw(Derived3 Base2); sub new() { my $s = Derived3->new(...); $s->{'__Derived4_datum1'}=(...)' $s->datum2(init-set); #calls Derived2's override method bless $s, $class; #need handle for Base2_GUI, but can't store in "$s", so need field + to #store it in; use local "$w" for window reference my $w=$s->{'__Derived4_Base2_GUI_handle'}=Base2_GUI->new(); $w->init_GUI(); $w->make_window(...) return $s; } sub Derived4_datum1 (){ return [or set] shift->{'_Derived4_datum1'}'; } sub re_init_GUI(){ my ($s=shift)->{'__Derived4_Base2_GUI_handle'}->init_GUI(); $s->{'__Derived4_Base2_GUI_handle'}->make_window(); } } #end package Derived4 { package User; our @ISA=qw(Derived4); sub new(){ my $c=shift; return bless Derived4->new(), ref $c || $c; } sub Do_Stuff(){ my $s=shift; my $dat0 = $s->datum0; # (pulls value from Mostest_Basest...) my $dat1 = $s->datum1; # (pulls value from Derived1) my $dat2 = $s->datum2(myval); # sets datum2 via "Derived2" { # then problems...next statement should "blow up" $s->relabel_window("$dat1, $dat2, $dat3"); $s->refresh_windows(); # also must blow up } return; } }
      That's the problem -- "User" is blind to Derived4's internal structure and doesn't know that the "window" routines would need to be called through the internally stored 2nd 'new' (constructor). I can't return BOTH handles to two separate objects, so "$s" really won't access methods in "Derived4's" 2nd base class (Base2) -- *correctly*. Perl may call Base2_GUI's methods because of the @ISA(?), but it will try to pass the "$s" value as a handle instead of the "$w" value ($s->{'__Derived4_Base2_GUI_handle'}) that Base2_GUI will be expecting.

      The following would also be incorrect to substitute for the extra-indented code above:

      { # also won't work (if privacy enforced) my $w=$s->{'__Derived4_Base2_GUI_handle'}; $w->relabel_window("..."); $w->refresh_windows("...); }
      even if it did work -- since "User" shouldn't know about Derived4's internal values.

      UG!....(hope this was more clear; just that it was *so* blasted long to type in...*sigh*) :-)
      Linda