Re: A caller() by any other name
by Tanktalus (Canon) on Dec 01, 2008 at 23:49 UTC
|
I think the perlish syntax you're looking for is a closure/code reference. That is, instead of passing in a full object or type, you pass in a CODE reference that does whatever you need it to do. That code reference, being a closure, would have access to the calling object to do work with it, but any work done on the calling object would still live inside the calling object. That is, the code dealing with $foo would live in Foo.pm, not in Bar.pm.
package Foo;
sub do_it
{
my $self = shift;
my $do_it_to = shift;
$do_it_to->handler(sub { $self->logger(@_) });
}
# and other stuff, like sub logger.
package Bar;
sub handler
{
my $self = shift;
my $callback = shift;
for ($self->get_all_items())
{
$callback->($_);
}
}
Something like that.
If you find yourself needing more than one callback in a single function (such as handler above), you may be better off defining an API that the function needs, and requiring that an object that handles that API be passed in. Otherwise the function call will get ugly. | [reply] [d/l] |
Re: A caller() by any other name
by kyle (Abbot) on Dec 01, 2008 at 20:54 UTC
|
If I understand your question correctly, you have a situation in which you call $foo->a(), which calls $bar->b(), and in the second call, you want to get $foo. Is that right?
You could, of course, pass $foo as an argument. As you correctly note, doing that "violates one, or more, OO rules & best practice(s)." I think that's also true if you could get this info from caller or some analog. That is, your desire to know this info may reveal a design problem.
You go on to say that maybe this could be a class behavior that you can add to any classes that need it. I agree this is probably a better solution, if this kind of interface is really needed at all. Something as simple as Re: Proper way to create 'globals' might do the trick too.
| [reply] [d/l] [select] |
|
|
Indeed, dead right in one :-)
IMHO, extending (caller)[0] to return an instance (c/w a package) isn't a violation to the same degree since it would be incumbent on the called sub/method to use the information presented...the design problem, such as it is, is outlined in my OP - being the need to implement inter-class relationships (as defined in a class diagram), hence my suggested afterthought WRT a class to implement the relationship between the participating classes - thus...
- Each participating class registers itself with the Relationship class - not knowing, or indeed needing to know, about the other class(es) in the relationship.
- The Relationship class handles method invocations between the participating objects.
AFAICS, Re: Proper way to create 'globals' would only work for singleton/monadic classes - it would need some work to achieve the same end for classes having multiple instances.
A user level that continues to overstate my experience :-))
| [reply] [d/l] [select] |
|
|
sub called_by {
return ( caller 1 )[0]->caller;
}
| [reply] [d/l] [select] |
Re: A caller() by any other name
by xdg (Monsignor) on Dec 02, 2008 at 02:07 UTC
|
It's a little hard to know what you mean, but if you want Object A to call a method on Object B and have B know that A was the object that called it, then you can just pass A as an argument. In other words, you define a consistent API where the @_ array always contains self and the calling object.
sub foo {
my ($self, $caller, @targets) = @_;
for my $obj ( @targets ) {
$obj->bar($self, ... );
}
}
sub bar {
my ($self, $caller, @args) = @_;
...
}
The examples above don't do anything with $caller, but you can see how $self is included as an argument to the bar method call.
For a real-world example of this kind of thing, look at POE, which defines array-offset constants to pick out specific objects that are always passed as arguments. For example:
sub handle_event {
my ($kernel, $heap, $parameter) = @_[KERNEL, HEAP, ARG0];
...;
}
It seems a little clunky at first glance, but when used consistently across all POE modules, it actually starts seeming elegant.
-xdg
Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.
| [reply] [d/l] [select] |
|
|
TFT xdg,
Using defined offsets into arrays/lists, to my mind, has little to offer over the normal positional arg passing - if nothing else, AFAICT, PBP recommends avoidance of such measures. Personally, however, I prefer named args for long arg lists (as does PBP;-).
But thanx anyway for the reference - POE provided some interesting reading...
A user level that continues to overstate my experience :-))
| [reply] |
|
|
Using defined offsets into arrays/lists, to my mind, has little to offer over the normal positional arg passing
As I understand the idea in POE, one reason for it is forward compatibility. You can rearrange the order of positional arguments -- e.g. insert new ones between fixed arguments and a variable list of arguments at the end -- and since the constants are redefined at the same time, code using the defined offsets still just works.
IMO, PBP should be taken with (several) grains of salt. It's only one opinion about best practices and even the author says it's not an exclusive list of best practices. And there are some stunningly bad recommendations in there, too.
-xdg
Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.
| [reply] |
Re: A caller() by any other name
by kennethk (Abbot) on Dec 01, 2008 at 19:24 UTC
|
I'm confused - if I understand your question, caller already does what you want: caller.
Update:: Upon reread, do you mean identification of package/object in call? That's already in there with the passed parent object -> perltoot/perltooc covers similar concepts in identifying whether a method is called by an object or a class.
I'm probably just missing the boat on all this.
| [reply] |
|
|
Sadly, caller doesn't quite do what I want :-( Hence my question.
As you say, when called from a class method/package sub, (caller)[0] returns the package name. Whereas what I'm suggesting it could return is an object reference e.g. <pkg>=HASH(0x123456) when the caller is an object.
Hope that makes it clearer.
A user level that continues to overstate my experience :-))
| [reply] [d/l] [select] |
|
|
Hope you are still checking the thread. Having read your comments and those that followed, I think I grok what you are getting at. Assuming you are not threading your code, you could essentially add a line at the start of every subroutine that caches the class/object reference of the call in a package global. You could then use caller to id the package, and the local variable to id what the last object used was. Clearly not elegant, and a horrible violation of OO ideals, but would work and shouldn't be too rough to create.
| [reply] |
Re: A caller() by any other name
by ikegami (Patriarch) on Dec 03, 2008 at 02:40 UTC
|
What you want is the first argument of the caller, if the caller was called as a method. Without going into the merits of doing so, getting the first argument of the caller appears to be possible (see following output), but I think Perl doesn't track if subroutines were called as methods.
>perl -MCarp -e"sub g { carp } sub f { shift->g } bless({})->f"
at -e line 1
main::g('main=HASH(0x235f7c)') called at -e line 1
main::f('main=HASH(0x235f7c)') called at -e line 1
If you don't want to pass the pointer explicitly, you'll need to provide a framework that does it for you.
Perl doesn't do OO. Perl has features that allow people to build OO systems on top of Perl.
- dragonchild, promoting Moose
| [reply] [d/l] |
|
|
| [reply] |
Re: A caller() by any other name
by DrHyde (Prior) on Dec 03, 2008 at 11:09 UTC
|
I'm not aware of any module that does exactly what you want, and I think the only way to do what you want is to grovel around in the perl internals to find and pick apart the stack and hopefully follow some pointers from there to the calling context. This is the sort of spectacularly evil hack that I would love to see on the CPAN but lack the XS and perlguts skills to do myself. | [reply] |
Re: A caller() by any other name
by pemungkah (Priest) on Dec 05, 2008 at 02:30 UTC
|
Okay, a really twisted way of getting the information.
The debugger extends what caller() returns if caller is called from the DB:: namespace:
$package:
The package name the sub was in
$filename:
The filename it was defined in
$line:
The line number it was defined on
$subroutine:
The subroutine name; '(eval)' if an eval().
$hasargs:
1 if it has arguments, 0 if not
$wantarray:
1 if array context, 0 if scalar context
$evaltext:
The eval() text, if any (undefined for eval BLOCK)
$is_require:
frame was created by a use or require statement
$hints:
pragma information; subject to change between versions
$bitmask:
pragma information: subject to change between versions
@DB::args:
arguments with which the subroutine was invoked (which is what you want).
You may be able to do what you want by defining an xcaller function that does this:
sub callers_args {
return DB::caller_caller(@_);
}
sub DB::caller_caller {
my($level) = @)_;
local @DB::args;
caller($level+1);
return @DB::args;
}
This is of course pure evil. Localizing @DB::args may let you run this under the debugger, but be prepared for weird and wild situations. You may need to turn off single-stepping in DB::caller_caller if you do try running under the debugger using $DB::single.
Again, if you used this in production code, you would be completely mad. And of course the other consideration: if you need to do this, you're not thinking about the problem the right way. What really needs to happen - forgetting completely about the implementation?
(correction 12/5/08: formatting was awful. Bad perlmonk. No biscuit.) | [reply] [d/l] |
|
|
| [reply] |