in reply to Extending a class

Hi betacentauri,

In sub Bar, you're calling $g->Foo, where $g is the return value of $self->group. Looking at the code of SVG::Element, that's a new SVG::Element. SVG::Element doesn't have a sub Foo, which is why the call fails.

As suggested by the AM, one solution is to patch the methods into SVG::Element by defining them as sub SVG::Element::Foo { ... }, MyPackage isn't necessary in that case. (Note however that a new constructor for MyPackage is not required, as the AM seems to be saying; the constructor is indeed inherited from SVG, which in turn calls its parent constructor in SVG::Element, but since they are coded appropriately they give you an object of type MyPackage.) It's kind of inelegant but it works. If you're only adding a few methods, it might be an acceptable solution.

Other than that, since SVG::Element::tag() is hardcoded to return a new object of type SVG::Element, I can't at the moment think of a solution that doesn't involve either updating the SVG package itself, or some kind of inelegant hacking/patching (perhaps another monk has another alternative handy).

Update: You can also fix the immediate problem by writing return $g->MyPackage::Foo("g$barArg"); in Bar. This doesn't stop Foo and Bar from returning objects of type SVG::Element, but at least it gets your example code working... now I'm out of ideas for tonight. (I also tried reblessing the return values, but that caused the SVG to be missing elements, which I haven't looked into yet.)

Update 2: SVG::Element uses the code if ( ref($k) =~ /^SVG::Element/ ) (instead of isa) to check whether objects are of its type. I'd say this and the above make it too difficult to do normal subclassing.

Hope this helps,
-- Hauke D

Replies are listed 'Best First'.
Re^2: Extending a class (updated)
by betacentauri (Sexton) on Apr 13, 2016 at 21:59 UTC
    In sub Bar, you're calling $g->Foo, where $g is the return value of $self->group. Looking at the code of SVG::Element, that's a new SVG::Element. SVG::Element doesn't have a sub Foo, which is why the call fails.

    Yes! I was aware of that. But I was expecting that, since $self was a MyPackage object, its progeny would be the same species...

    Anyway the SVG::Element::Foo trick seems to work, thank you all very much for your responses!

      Hello betacentauri,

      But I was expecting that, since $self was a MyPackage object, its progeny would be the same species...

      Yes, they would be. So within your sub Bar, $self is an instance of class MyPackage; and since MyPackage ISA SVG, $self ISA SVG too.

      But don’t be fooled by the names: as far as Perl is concerned, SVG and SVG::Element are separate and unrelated classes! When Perl sees use SVG; (or use parent 'SVG';), it looks for a module named SVG.pm in the directories contained in the global variable @INC. But when it sees use SVG::Element;, it looks in @INC for a directory named SVG, and then within that directory for a module named Element.pm. And that’s it! Apart from their namespaces, the two modules are independent of each other. See perlmod#Packages.

      Of course, these classes are related conceptually (i.e., in the programmer’s mind), which is why their author included them both in the SVG distribution (which also contains classes SVG::DOM, SVG::Extension, and SVG::XML); but Perl itself knows nothing of this relationship.

      Update: The above is true in the general case. But in this specific case, it turns out that the module SVG.pm actually begins as follows:

      package SVG; use strict; use warnings; use SVG::XML; use parent qw(SVG::Element SVG::Extension);

      — so an object of class SVG ISA SVG::Element as well. Thanks to haukex for the heads-up.

      Hope that helps,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hi betacentauri,

      I was expecting that, since $self was a MyPackage object, its progeny would be the same species...

      my $tag = new SVG::Element( $name, %attrs ); is hardcoded in SVG::Element::tag() (called directly from SVG::Element::group()), so it doesn't depend on $self, it's always calling SVG::Element::new(). It could certainly be argued that SVG::Element could be coded differently to enable the kind of subclassing you want to do!

      Regards,
      -- Hauke D