bamaindk has asked for the wisdom of the Perl Monks concerning the following question:

We were using a module in our system which was throwing exceptions and killing our script. We could not change the module being used. So we needed to wrap it in a new module to catch the exceptions so we could continue.

The new(wrapper) module would re-implement the functions provided by the exception throwing module and simply call the wrapped functions inside an eval plus some stuff. That way the impact to our system would be minimal and transparent as we could just swap out the 'use' in the affected parts with the new module.

Then I had the idea to make the new module inherit from the exception throwing module and override the functions that threw an exception instead of wrapping them. I thought this would give the benefit of providing all of the functionality already supplied by the module without me having to wrap everything within it, which was rather extensive. Any non implemented functions would simply be passed to the base class. Whereas the wrapping would only give what was supplied in the new module and any non implemented functions would end in an exception. Aahh! Object orientation and re-use:-)

So I did something like this in my constructor.

my $class = shift; my $self = ExceptionThrowingModule->new(args); $self->{_myOwnattributes} = $this; $self->{_myOwnattributes} = $that; bless($self, $class);
And within the different overridden functions I called the base class. like this
sub funcA{ eval { $self->SUPER::funcA(); }; _handle_eval($self); }
I implemented the inheritance and everything seemed to work splendidly until I ran into this! In one of the ExcpetionThrowingModules methods, that I had overridden, it did this.
funcA{ $self = shift; Yada Yada Yada.... $self->funcB();<--- Which I had also overridden!! }
This ended up calling back to my overridden funcB() method as the hash passed in is blessed into my class.
So, it seems to me, I just broke the class I inherited from without illegally access it or anything:-(

Does this mean that you can never safely inherit and override a method in Perl?
When SUPER is used to look up an ancestor should it not also ensure the proper object type?
Does anyone have a solution for how to do this?

Replies are listed 'Best First'.
Re: Overriding methods and Inheritance
by dragonchild (Archbishop) on Jan 07, 2008 at 15:03 UTC
    This is one of those cases where inheritance isn't necessarily the right way to do things, even though it seems ideally suited. What you really want is delegation, specifically you want to decorate the ExceptionThrowingModule with your own module. So, something like so:
    package MyModule; use OtherModule; sub new { my $class = shift; my $obj = OtherModule->new( @_ ); return bless \$obj, $class; } sub AUTOLOAD { my $self = shift; my ($method) = (our $AUTOLOAD =~ /([^:]+)$/; return eval { ${$self}->$method( @_ ); } } # These are in UNIVERSAL, so need to be overridden because # AUTOLOAD won't catch them. sub can { my $self = shift; return eval { ${$self}->can( @_ ); } } sub isa { my $self = shift; return eval { ${$self}->isa( @_ ); } } 1; __END__
    There's more you'd have to do in order to make sure you play properly with overload and others. But, that's the gist of it. And, the runtime cost is quite minimal, probably on the order of 1-2%. And, you're very loosely couple with OtherModule, meaning that as OtherModule changes, you don't have to change with it.

    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?
      Yes, I think you are correct in that I probably should have chosen the delegate pattern. Which I think is in reality aggregation, yes? Thanks for the help and the solution proposal:-)
        Delegation isn't aggregation. Aggregation is taking many things and calling them by one name, like an array or hash. Delegation is taking one thing and using some other one thing to talk to it, often pretending the latter is the former (which would be more properly called decoration).

        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?
Re: Overriding methods and Inheritance
by friedo (Prior) on Jan 07, 2008 at 14:39 UTC

    I don't quite understand why calling the version of funcB from your child class is bad. That's the expected behavior of overridden functions, and if I understand what you're doing correctly, it will just wrap the calls in multiple levels of eval.

    I think you've discovered, though, at least one reason why this solution to your problem is extremely cumbersome. After all that work, would it have really been that hard to use evals in the original scripts that were using the exception throwing module in the first place?

Re: Overriding methods and Inheritance
by dsheroh (Monsignor) on Jan 07, 2008 at 17:25 UTC
    This ended up calling back to my overridden funcB() method as the hash passed in is blessed into my class.

    Yes. Yes it does. That's the way polymorphism works and is nothing specific to Perl.

    package Dog; sub pull_tail { $self->speak; } sub speak { print "Woof!\n"; } package AngryDog; sub speak { print "Grrrrr...\n"; }
    What would you expect to happen when you pull an AngryDog's tail? That it would bark (because pull_tail is defined by the base Dog class) or that it would growl (because that's what AngryDogs say)? It's an AngryDog, so it should respond accordingly.

    (If you wanted all Dogs to react like a base Dog instead of according to their subclass, you could force them to always use the base Dog::speak by changing the $self->speak line to Dog::speak($self), but that's done very rarely, as there are very few cases where it's what you actually want.)

      Yes, I can see and understand that logic. I just didn't think that the class I inherited from would know anything about a child. Statically it does not but runtime what is being passed around is as you said an "AngryDog". I was/am confusing static model with the dynamic one.

      So for this to work I need to "morph" AngryDog to a Dog prior to asking SUPER to handle. It might even be reasonable to expect "$self->SUPER" to do this.

      I think I might also have selected the wrong mechanism, as stated in other reply to my question. Delegation seems maybe the better solution. I must admit though it sure did look like the right candidate for inheritance.

      I wonder what happens in this scenario in C++?

      Anyways, thanks for the help:-)

        It might even be reasonable to expect "$self->SUPER" to do this.

        Just to extend my example a little to include that, let's add another method to AngryDog:

        sub pull_tail { $self->SUPER; bite(); }
        In real-world logic, it's obvious that the AngryDog should still growl before biting when its tail is pulled, not bark.

        It's been quite a while since I last used C++, but I talked to a Java junkie friend the last time something like this came up and he confirmed that Java handles it the same as Perl does. I'm pretty sure C++ (and, for that matter, any language that supports polymorphism) would also behave similarly. To do otherwise would render abstract classes/methods pointless. (Derived class overrides the abstract method, base class method calls the abstract method, and... either nothing happens or an exception is thrown unless it calls the derived class's version instead of the base version.)

        But, yeah, I do agree that inheritance looks like an obvious candidate, but dragonchild's AUTOLOAD-based solution is surely both the slickest solution and the one which requires the least code to do what you want.