Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.99k stars 559 forks source link

UNIVERSAL inconsistencies #3077

Closed p5pRT closed 14 years ago

p5pRT commented 23 years ago

Migrated from rt.perl.org#5091 (status was 'resolved')

Searchable as RT5091$

p5pRT commented 23 years ago

From root@rt158.private.realtime.co.uk

Created by @pdcawley

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  

 

Perl Info ``` Flags: category=library severity=medium Site configuration information for perl v5.6.0: Configured by flepied at Sat Sep 30 18:19:36 CEST 2000. Summary of my perl5 (revision 5.0 version 6 subversion 0) configuration: Platform: osname=linux, osvers=2.2.17-16mdksmp, archname=i386-linux uname='linux debris.mandrakesoft.com 2.2.17-16mdksmp #1 smp thu sep 28 15:25:49 cest 2000 i686 unknown ' config_args='-des -Darchname=i386-linux -Dd_dosuid -Ud_csh -Duseshrplib -Doptimize=-O3 -fomit-frame-pointer -pipe -s -march=pentium -ffast-math -fexpensive-optimizations -Dprefix=/usr -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/lib/perl5/man/man3' hint=recommended, useposix=true, d_sigaction=define usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef useperlio=undef d_sfio=undef uselargefiles=define use64bitint=undef use64bitall=undef uselongdouble=undef usesocks=undef Compiler: cc='cc', optimize='-O3 -fomit-frame-pointer -pipe -s -march=pentium -ffast-math -fexpensive-optimizations', gccversion=2.95.3 19991030 (prerelease) cppflags='-fno-strict-aliasing' ccflags ='-fno-strict-aliasing -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64' stdchar='char', d_stdstdio=define, usevfork=false intsize=4, longsize=4, ptrsize=4, doublesize=8 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, usemymalloc=n, prototype=define Linker and Libraries: ld='cc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lnsl -ldl -lm -lc -lposix -lcrypt libc=/lib/libc-2.1.3.so, so=so, useshrplib=true, libperl=libperl.so Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic -Wl,-rpath,/usr/lib/perl5/5.6.0/i386-linux/CORE' cccdlflags='-fpic', lddlflags='-shared -L/usr/local/lib' Locally applied patches: @INC for perl v5.6.0: /usr/lib/perl5/5.6.0/i386-linux /usr/lib/perl5/5.6.0 /usr/lib/perl5/site_perl/5.6.0/i386-linux /usr/lib/perl5/site_perl/5.6.0 /usr/lib/perl5/site_perl . Environment for perl v5.6.0: HOME=/home/pdcawley LANG=en LANGUAGE=en_GB:en LC_COLLATE=en_GB LC_CTYPE=en_GB LC_MESSAGES=en_GB LC_MONETARY=en_GB LC_NUMERIC=en_GB LC_TIME=en_GB LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/home/pdcawley/pub/i686-pc-linux-gnu/bin:/home/pdcawley/pub/common/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/local/bin:/usr/games:/home/pdcawley/bin PERL_BADLANG (unset) SHELL=/usr/bin/zsh Flags: category=library severity=medium Site configuration information for perl v5.6.0: Configured by flepied at Sat Sep 30 18:19:36 CEST 2000. Summary of my perl5 (revision 5.0 version 6 subversion 0) configuration: Platform: osname=linux, osvers=2.2.17-16mdksmp, archname=i386-linux uname='linux debris.mandrakesoft.com 2.2.17-16mdksmp #1 smp thu sep 28 15:25:49 cest 2000 i686 unknown ' config_args='-des -Darchname=i386-linux -Dd_dosuid -Ud_csh -Duseshrplib -Doptimize=-O3 -fomit-frame-pointer -pipe -s -march=pentium -ffast-math -fexpensive-optimizations -Dprefix=/usr -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/lib/perl5/man/man3' hint=recommended, useposix=true, d_sigaction=define usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef useperlio=undef d_sfio=undef uselargefiles=define use64bitint=undef use64bitall=undef uselongdouble=undef usesocks=undef Compiler: cc='cc', optimize='-O3 -fomit-frame-pointer -pipe -s -march=pentium -ffast-math -fexpensive-optimizations', gccversion=2.95.3 19991030 (prerelease) cppflags='-fno-strict-aliasing' ccflags ='-fno-strict-aliasing -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64' stdchar='char', d_stdstdio=define, usevfork=false intsize=4, longsize=4, ptrsize=4, doublesize=8 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, usemymalloc=n, prototype=define Linker and Libraries: ld='cc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lnsl -ldl -lm -lc -lposix -lcrypt libc=/lib/libc-2.1.3.so, so=so, useshrplib=true, libperl=libperl.so Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic -Wl,-rpath,/usr/lib/perl5/5.6.0/i386-linux/CORE' cccdlflags='-fpic', lddlflags='-shared -L/usr/local/lib' Locally applied patches: @INC for perl v5.6.0: /usr/lib/perl5/5.6.0/i386-linux /usr/lib/perl5/5.6.0 /usr/lib/perl5/site_perl/5.6.0/i386-linux /usr/lib/perl5/site_perl/5.6.0 /usr/lib/perl5/site_perl . Environment for perl v5.6.0: HOME=/home/pdcawley LANG=en LANGUAGE=en_GB:en LC_COLLATE=en_GB LC_CTYPE=en_GB LC_MESSAGES=en_GB LC_MONETARY=en_GB LC_NUMERIC=en_GB LC_TIME=en_GB LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/home/pdcawley/pub/i686-pc-linux-gnu/bin:/home/pdcawley/pub/common/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/local/bin:/usr/games:/home/pdcawley/bin PERL_BADLANG (unset) SHELL=/usr/bin/zsh ```
p5pRT commented 14 years ago

From @cpansprout

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?

p5pRT commented 14 years ago

@cpansprout - Status changed from 'open' to 'resolved'