Closed p5pRT closed 14 years ago
I've been mucking around with container classes in an application here. What I want to be able to do is to have my container masquerade as the thing it contains (aka a Decorator if you do the pattern language stuff)
Aha! I thought\, I can just do this:
sub isa { my($self\, $class) = @_; $self->SUPER::isa($class) || ref($self) && eval {$self->item->isa($class)}; }
sub can { my($self\, $method) = @_; my $can = $self->SUPER::can($method); return $can if $can; $can = ref($self) && eval {$self->item->can($method)}; return unless $can; return sub { shift; return $can->($self->item\,@_); } }
sub AUTOLOAD { my $method = $AUTOLOAD; $method =~ s/.*:://; return if $method eq 'DESTROY'; my $self = shift; return eval {item->can($method)} ? $self->item->$method(@_) : $self->${\("SUPER::$method")}(@_); }
Which\, whilst not being exactly easy\, isn't really rocket science either\, and I would argue that (admittedly via a mechanism other than @ISA) this object inherits from both its own @ISA tree and from the package of the object that it contains. And hey\, if there's more than one way to do it\, there can surely be more than one way to inherit.
And it works\, with one (depressingly large) caveat: It doesn't work if any client code uses UNIVERSAL::isa directly.
Aha! I'll just overload UNIVERSAL::isa and UNIVERSAL::can then. Well\, yeah\, but the reason for using Container::isa and Container::can was that I didn't want to do anything quite so drastic.
Ideally\, I want the UNIVERSAL 'isa' and 'can' to check at each step up the inheritance tree if there's a 'local' implementation at that level that isn't the UNIVERSAL one\, and if there is\, to stop walking any further up that branch of the tree and see what the local implementation says. I can do this in Perl\, and I will if I have to\, but it's not going to be anything approaching fast and I'm afraid I don't have the C skills to supply a patch.
Yes\, I know there are other ways to do this without having to roll a bunch of specialist container classes but they all involve rather more messing with the symbol table than I like\, along with a requirement to rebless the container every time its contents change\, which just seems Wrong.
Also\, while investigating this I came across the following piece of weirdness\, which seems to run contrary to the documentation of UNIVERSAL.
#perl -l print '1. '\, UNIVERSAL::isa('Foo'\, 'UNIVERSAL'); # Expect 1 print '2. '\, Foo->isa('UNIVERSAL'); # Expect 1 print '3. '\, UNIVERSAL::isa('Foo'\, 'UNIVERSAL') # Expect 1
outputs:
1. 2. 1 3. 1
On Fri Jan 05 06:19:15 2001\, root@rt158.private.realtime.co.uk wrote:
And it works\, with one (depressingly large) caveat: It doesn't work if any client code uses UNIVERSAL::isa directly.
Since you reported this\, the documentation has been updated. UNIVERSAL now says:
# but never do this! $is_io = UNIVERSAL::isa($fd\, "IO::Handle"); $sub = UNIVERSAL::can($obj\, "print");
and highly recommends only calling those as $thing->isa/can(...).
Is that a sufficient fix?
@cpansprout - Status changed from 'open' to 'resolved'
Migrated from rt.perl.org#5091 (status was 'resolved')
Searchable as RT5091$