Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re^5: Beyond Inside-Out (class)

by tye (Sage)
on May 29, 2007 at 17:46 UTC ( #618021=note: print w/replies, xml ) Need Help??


in reply to Re^4: Beyond Inside-Out (details)
in thread Beyond Inside-Out

what I'm using is the package that was in force when the call to ego() was compiled. That is excatly what I want: Mehods compiled in a class access the alter ego specific to that class, and that's the only way.

Yes, you are using the package (not "class") that the method was compiled in. But, no, you appear to have nearly completely missed my point. The package that a subroutine was compiled in can have very little to do with the class that the method ends up being a part of. In my example, the "class" you'd end up using isn't even a class, just the name of a package where code for some subroutines happened to be compiled.

The correct thing to look up is not what package the code was compiled into, nor the package part of the orignal name given to the subroutine (the two things that caller can give you). The correct "class" is the package part of the subroutine name that was used to find the subroutine when the method was looked up (looking in symbol tables and following @ISA). Unfortunately, I have yet to see a way to get this information. That is why you'd need to provide a way to override how the class name is determined.

Ah, so you are using Perl "magic" and you are allowing for "uncooperative" subclassing. If you'd include the XS code then this would have been clearer. I can't comment much on this because I'd still be guessing at what you are really doing (no, I'm not going to download the package and extract the source this morning), though I consider using "magic" to take much of the "simpler" benefit away from your idea.

- tye        

Replies are listed 'Best First'.
Re^6: Beyond Inside-Out (class)
by Anno (Deacon) on May 29, 2007 at 19:50 UTC
    Yes, you are using the package (not "class") that the method was compiled in. But, no, you appear to have nearly completely missed my point.

    Entirely possible.

    The package that a subroutine was compiled in can have very little to do with the class that the method ends up being a part of.

    True, but I'm not too worried about that. It doesn't happen often in practice and is usually just done for convenience and can be avoided. But yes, it's something a user would have to be warned about. I am indeed assuming that the code for method Foo::meth was compiled with package Foo in force.

    The correct "class" is the package part of the subroutine name that was used to find the subroutine when the method was looked up (looking in symbol tables and following @ISA). Unfortunately, I have yet to see a way to get this information.

    You mean, when a method is called qualified, as in $obj->Foo::meth? Two things can happen: The method is actually found in class Foo, in which case I assume its code was also compiled there, so that's okay. Or Foo itself inherits the method from Bar, in which case the Bar's incarnation of the object is used. That is also how it's supposed to work.

    That is why you'd need to provide a way to override how the class name is determined.

    You haven't quite convinced me with the specific argument, but general experience shows that for all caller-sensitive functions there comes a time when you want to override the caller. Run-time compiling code in the right package (sometimes the only way out) is too ugly. I guess I'll provide a way to specify a class different from your own just in case.

    I have appended the XS code for Alter::ego, it isn't that much.

    Anno

    /* id-key for ext magic */ #define ALT_EXT_ALTER 6693 MODULE = Alter PACKAGE = Alter SV* ego(...) PROTOTYPE: $@ CODE: SV* obj = ST(0); SV* given = items > 1 ? ST(1) : NULL; HV* alt_tab; SV** alt_ptr; if (SvROK(obj)) { char* class = CopSTASHPV(PL_curcop); SV* self = SvRV(obj); MAGIC* mg; if (SvTYPE(self) < SVt_PVMG) SvUPGRADE(self, SVt_PVMG); for (mg = SvMAGIC(self); mg; mg = mg->mg_moremagic) { if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == ALT_EXT_ALTER) ) break; } if (!mg) { alt_tab = newHV(); mg = sv_magicext(self, (SV*)alt_tab, PERL_MAGIC_ext, NULL, + NULL, 0); mg->mg_private = ALT_EXT_ALTER; } else { alt_tab = (HV*)mg->mg_obj; } if (alt_ptr = hv_fetch(alt_tab, class, strlen(class), 0)) { RETVAL = newRV_inc(SvRV( *alt_ptr)); } else { if (!given) { /* should probably croak, but we decree a ha +sh */ given = newRV_inc((SV*)newHV()); } else { /* need a non-mortal ref */ given = newRV_inc(SvRV(given)); } hv_store(alt_tab, class, strlen(class), SvREFCNT_inc(given +), 0); RETVAL = given; } } else { RETVAL = NULL; } OUTPUT: RETVAL
Re^6: Beyond Inside-Out (class)
by shmem (Chancellor) on May 29, 2007 at 20:26 UTC
    The correct thing to look up is not what package the code was compiled into, nor the package part of the orignal name given to the subroutine (the two things that caller can give you). The correct "class" is the package part of the subroutine name that was used to find the subroutine when the method was looked up (looking in symbol tables and following @ISA). Unfortunately, I have yet to see a way to get this information. That is why you'd need to provide a way to override how the class name is determined.

    You mean, runtime overriding methods for an object after the fact of its construction by manipulating the @ISA chain, while still expecting access to the original object data? Could you name me some Modules which do that devilry so I can immediately scratch them from my list of useful modules, if only for lack of understanding and never wishing to have to debug them? Or is it just that I can't follow?

    If you mean altering the inheritance chain to make the object mutate and show a distinct behaviour depending on the current context, that works fine with Anno's approach.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      You mean, runtime overriding methods for an object after the fact of its construction by manipulating the @ISA chain, while still expecting access to the original object data?

      Uh, no, I don't mean that; not anything even close to that. How did you get this from the code I posted?

      Submit this search to see some examples of one technique I'm familiar with that would cause the "problem". I suspect there are quite a few other techniques that would cause the same "problem", and I recall a recent thread where someone had the "SUPER:: doesn't work because caller() gives the wrong package" problem so I'm not the only person on the planet who has run into this.

      It sounds like Anno has even seen this problem because s/he notes that the work-around of eval "package $pkg; sub $method { $code }" ... is burdensome.

      I'm not making any claim to how common any such techniques are on CPAN (I don't search CPAN modules and compile statistics on which techniques are in use). If that is your criteria, then I should not have any qualms about doing things that would break Anno's approach because it isn't being used anywhere at all on CPAN. :)

      I care because I consider some of these techniques to be good practices.

      - tye        

        Submit this search to see some examples of one technique I'm familiar with that would cause the "problem".

        Picking an example stub and populating it with code...

        package Foo::Bar; use vars qw( $VERSION ); sub method { my $self = shift; my $ego = __PACKAGE__.'::_implement::helper'; print "Foo::Bar::method: method = ", $ego->($self),"\n" } package Foo::Bar::_implement; use Alter qw(ego); sub helper { ego( shift)->{'method'}; } sub Foo::Bar::objectPackage { "Foo::Bar::Object"; } sub Foo::Bar::new { my $class = shift; warn "Foo::Bar::new(@_), class = $class\n"; my $thingy = bless \ my $ref, $class; ego ($thingy,{@_}); print "new thingy = $thingy\n"; $thingy; } sub Foo::Bar::Object::method { my $self = shift; print "Foo::Bar::Object::method: \$self = $self\n"; my $corona = ego( $self); print "Foo::Bar::Object::method: method = ",$corona->{'method'},"\ +n"; print "helper returns ",helper($self),"\n"; } 1;
        #!/usr/bin/perl my $foo = Foo::Bar->new(method => 'helper'); print "foo isa $foo\n"; $foo->method; my $meth = $foo->objectPackage.'::method'; $foo->$meth; __END__ Foo::Bar::new(method helper), class = Foo::Bar new thingy = Foo::Bar=SCALAR(0x8205ba4) foo isa Foo::Bar=SCALAR(0x8205ba4) Foo::Bar::method: method = helper Foo::Bar::Object::method: $self = Foo::Bar=SCALAR(0x8205ba4) Foo::Bar::Object::method: method = helper helper returns helper

        hmm... well (I might not have it populated like you would do :-). The Foo::Bar::new method is compiled into the package Foo::Bar::_implement, so a Foo::Bar object's class stash (or corona) will not be visible within methods compiled directly into the package Foo::Bar, and the only way is to crank the object through the "helper" sub above. But even if Foo::Bar is compiled into Foo::Bar::_implement, the corona can be forced into "the right package" (Foo::Bar) by swichting the package in the constructor:

        package Foo::Bar::_implement; sub Foo::Bar::new { my $class = shift; my $thingy = bless \ my $ref, $class; package Foo::Bar; ego ($thingy,{@_}); package Foo::Bar::_implement; $thingy; }

        which might also not be what you want. But somehow it must be decided what the "class" is supposed to be - is it the package or the subroutine qualifier? If it is the latter - quoting you from a previous note,

        The correct "class" is the package part of the subroutine name that was used to find the subroutine when the method was looked up (looking in symbol tables and following @ISA). Unfortunately, I have yet to see a way to get this information.
        how could that be different from what $class is in ($class = (caller(0))[3] ) =~ s/::[^:]+$// ?

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://618021]
help
Chatterbox?
[Tanktalus]: or posting the last hour of cb... well, maybe I should double check that first :)
[Tanktalus]: yup, it's working. :)
[choroba]: https://github. com/choroba/pm-cb/ commit/7b57f513596 7bf8a29d74f1c307de 9a76894cdcf
[choroba]: Also, a thread here on PM mentioned that one of perlmonks.com or www.perlmonks.com should now work
[choroba]: Tidings
[Tanktalus]: So, I can log in, I can update last hour of cb, I can read the cb, I just can't post a message to it :(

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (5)
As of 2018-07-15 21:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?















    Results (326 votes). Check out past polls.

    Notices?