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

I have a call-graph that doesn't seem to work with the standard or 'c3' mro's:

<main>____ / \ \ <CP> <SW> <V(main)> \ \ <V(CP)> <V(SW)> Note: 'V(x) != V(y)'

Different error messages for the default and c3 mro's:

mro:c3: Inconsistent hierarchy during C3 merge of class 'SW': current merge results [ SW, ] merging failed on 'D' at src line. main::BEGIN() called at src line eval {...} called at src line BEGIN failed--compilation aborted at src line. Inconsistent hierarchy during C3 merge of class 'SW': current merge results [ SW, ] merging failed on 'D' at src line during global destruction. mro:default: main::ISA=["CP", "SW", "D"] Can't locate object method "cp_new" via package "D" at src line. at src line.

Also tried adding CP onto 'end' of @ISA, as the ClientProc has the "cp_new" method I need, but that didn't work either.

Is there a mro that supports (or can be configured to support) a D-module being called multiple times in the hierarchy. Of note: the calls creating 'D', pass different args to "new", that define different subs-by-name in the caller.

In some way, it's like a call to exporter -- with the EXPORTS list (et al.) being a parameter list of subs to export to future users of the original module.

Solutions to make the above work would be very welcome.

Thanks!

Replies are listed 'Best First'.
Re: mro's which to use for flexibility?
by Corion (Patriarch) on Feb 07, 2016 at 08:24 UTC

    I would not use inheritance but maybe aggregation in your case.

    If aggregation does not fit the problem pattern, maybe "roles" or simply manually importing the parametrized methods from CP and SW into the main class is what you want.

    Without seeing a minimal, self-contained example that also explains what the intended result is, I can only give vague hints.

      Actually, you gave a direct hint. "I would not use inheritance...", which seems like it might be a hint that there is no easy way to do what I want using normal inheritance. Or is my interpretation incorrect? I'd love it if it was. ;-)

      OTOH, maybe you can explain what you mean by the terms 'aggregation' and 'roles'. I may understand the concepts, but maybe not by those names, or such. For example, I'd think of aggregation being something like combining multiple copies of the duplicate module into the modules that use them, which feels wrong on multiple levels, so I move to my fallback position of claiming ignorance of the how you define and use those terms.

      Part of the problem probably comes from my manually "pushing" or "unshifting" the key module where the 'open' is:

      use Data::Vars [ qw(dry_run overrides todos) ], {dry_run=>0, todos=>[], overrides=>{} }; use Switches; BEGIN{unshift @ISA,"TorClient";} our $mp = Data::Vars->new(); P "main::ISA=%s", \@ISA; $mp->trm_open('trm', 'localhost:1024');
      Which produces this output:
      main::ISA=["TorClient", "Switches", "Data::Vars"] Can't locate object method "trm_open" via package "Data::Vars" at ./to +r_mgr line 360. at ./tor_mgr line 360.

      Of note, trm_open is in the 1st package listed in @main::ISA.

      On the surface, it seems like the normal "search for subs in @ISA packages and descendants, w/that order based on the mro.

      Oddly enough, method resolution **rarely** need look into the DV package at any level, since it exports the "access routines" for the varnames specified in the first array ref. There are a few, little-used, util routines in that module to allow merging, handling 'new' to create an initialized copy of the vars in the passed and/or returned pointer. I could experiment with not including Data::Vars in the ISA list(s), but as the D::V module is used in a few over 700 places in various progs and modules in my bin dir, testing for side-effects would be a real pain.

      Maybe your mentioned methods might provide alternate ideas... Either that, or first thing I might attempt is clearing the mro cache before that 1st call.

      A second idea (which is a big "unknown" for me), is creating an mro that works with dynamic ISA alteration)....

        Your code is highly confused, or at least does not highlight where the problem you describe is.

        BEGIN{unshift @ISA,"TorClient";} our $mp = Data::Vars->new(); P "main::ISA=%s", \@ISA; $mp->trm_open('trm', 'localhost:1024');

        The altering of @ISA only alters @main::ISA, but the class you're dealing with is of type Data::Vars, whose inheritance you have not altered at all.

        I suggest you learn about namespaces and importing subroutines instead of trying to solve problems by abusing inheritance.

        What prevents you from writing a class My::TorClient, which defines all the methods you want?

        Update: Also see Aggregation, which points to Object composition, and Moose::Manual::Roles. But I think in the end you will be much better served by writing simple subroutines that call the functions you want to call.