Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Re^4: Beyond Inside-Out (details)

by Anno (Deacon)
on May 29, 2007 at 15:57 UTC ( #617988=note: print w/replies, xml ) Need Help??


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

Something like:
sub ego { $_[0] ||= {}; my $corona= shift @_; my $pkg= caller(); for my $ego ( $corona->{$pkg} ) { $_= shift @_ if @_; $_ ||= {}; return $_; } }
Yes, a much simpler and more direct solution to some of the problems that "inside-out" objects are being promoted to address1.

Yes, that's similar with one important difference: In your implementation, the object proper is its corona (a hash). The way I have it in Alter, the object itself can be whatever it is, the corona is attached via magic, and accessed through the magical ego() function.

That leaves the possiblity for the object proper (the "carrier") to be whatever it needs to be to support a conventional class. That answers the question of mixed inheritance. Like with inside-out, any class can inherit from an Alter-based class, and in reverse, an Alter-based class can inherit (without further ado) from one conventional class. Both work by making the conventioonal object the carrier.

As for method-borrowing, a la

package Foo; *brrowed_meth = \ &Bar::meth;
why, yes, the borrowed method would continue to access the alter ego that was established in Bar. That's what I would expect in any case.

As to the more general problem of the various meanings of caller, if I'm not mistaken 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.

Anno

Replies are listed 'Best First'.
Re^5: Beyond Inside-Out (class)
by tye (Sage) on May 29, 2007 at 17:46 UTC
    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        

      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
      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        

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://617988]
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (7)
As of 2018-06-23 15:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?



    Results (125 votes). Check out past polls.

    Notices?