Re: Code references as use() parameters
by broquaint (Abbot) on Apr 09, 2003 at 15:38 UTC
|
Can't say I'm proud but ...
package pmsopw_249244;
sub import {
push @pmsopw_249244::USES, [scalar caller, @_[1 .. $#_]];
}
sub real_import {
$_->() for @_[1 .. $#_];
}
INIT {
for (@pmsopw_249244::USES) {
my($pkg, @args) = @$_;
eval (qq[{
package $pkg;
pmsopw_249244->real_import(\@args);
}]);
warn "ack - $@" if $@;
}
}
q{ the end is nigh ...};
And the code that uses the module
use pmsopw_249244 (\&func);
sub func {
print "I've been called\n";
}
__output__
I've been called
That might not be stellar code but hopefully it gives you some ideas.
HTH
_________ broquaint | [reply] [d/l] [select] |
Re: Code references as use() parameters (well)
by tye (Sage) on Apr 09, 2003 at 17:05 UTC
|
That works fine. You can take a reference to a subroutine by name before the subroutine exists. Perl does some interesting tricks to allow this. The problem you will run into is if you want to dereference it while still inside of import().
I can think of several ways around this. The very straight-forward:
require Alzabo::MethodMaker;
Alzabo::MethodMaker->import( name_maker => \@name_maker );
or you could make use of the undocumented fact that require actually returns the final "true" value in the module. So write your module like this:
package Alzabo::MethodMaker;
sub import {
...
}
\&import;
and use it like this:
(require Alzabo::MethodMaker)->(
name_maker => \&name_maker,
);
or you could require the subroutine to be declared in-line:
use Alzabo::MethodMaker(
name_maker => sub {
...
},
);
You could use the poorly documented INIT as broquaint demonstrated. INIT is so poorly documented that I'm not sure of the consequences of this approach but the demonstration makes me worry that the INIT will be run too late if you need methods created before the main script has finished compiling. Certainly some will say this is very unlikely, but then, some would say that the order-of-definition problem that you are running into is very unlikely.
That might be the most appealing solution because it hides so much from the module user so be sure to carefully document when the modules actually get "made" if you go this route.
You could export a routine that makes the methods:
use Alzabo::MethodMaker qw( makeMethods );
makeMethods(
name_maker => \&name_maker,
);
You could document that Alzabo::MethodMaker should appear last in the source code rather than the more traditional first (I can certainly see potential value for a MethodMaker module being able to see what other methods have already been defined).
In any case, good luck.
- tye | [reply] [d/l] [select] |
Re: Code references as use() parameters
by Tomte (Priest) on Apr 09, 2003 at 15:27 UTC
|
Putting the subs you need (that is baz) in a module of theire own and useing this before using alzabo isn't an option? Or using an anonymous sub?
Or am I just plain silly?
regards,
tomte
Hlade's Law:
If you have a difficult task, give it to a lazy person --
they will find an easier way to do it.
| [reply] |
Re: Code references as use() parameters
by MarkM (Curate) on Apr 09, 2003 at 16:25 UTC
|
The real problem isn't that the subroutine reference is taken before it exists, as this actually works due to the magic of Perl:
package A;
sub import { *b = $_[1] };
BEGIN { $INC{"A.pm"} = undef } # Stop 'use' from loading A.pm
package main;
use A \&c;
print A::b(), "\n"; # prints "YO\n"
sub c { "YO" }
I suspect your problem is that Alzabo::MethodMaker is trying to execute name_maker() before it is defined (otherwise, you wouldn't have this question, as the above code should 'work'). The only way around this is to define name_maker() first, or to delay execution of Alzabo::MethodMaker::import using one of the means suggested by other people. | [reply] [d/l] |
|
|
| [reply] |
|
|
I think I was clear that in the general case, as long as the subroutine is not invoked as part of the 'use', there is no problem. If the subroutine is invoked, then it needs to have already been defined. Only taking the reference of an as-yet-non-existent subroutine is not dangerous.
Actually, there is one danger, and it has to do with prototypes. If the prototypes differ (when the function actually is defined), Perl will croak.
| [reply] |
Re: Code references as use() parameters
by Improv (Pilgrim) on Apr 09, 2003 at 15:22 UTC
|
I don't have a direct solution, but one workaround would be
to create a function Alzabo::parameterize() which would
stuff that stuff away into the Alzabo:: namespace at
runtime, and have all your methods call a function to make
sure that parameters have been filled in before use. | [reply] |
Re: Code references as use() parameters
by nothingmuch (Priest) on Apr 09, 2003 at 15:41 UTC
|
perhaps converting use from BEGIN { require Module; import Module } to CHECK { require Module; import Module } can do the trick. But the drawbacks are obvious.
Depending on when the coderef is used perhaps you can pass a soft reference. But then you have to no strict refs within the used module, and any if (ref $whatever) tests will fail.
-nuffin zz zZ Z Z #!perl | [reply] |
Re: Code references as use() parameters
by John M. Dlugosz (Monsignor) on Apr 09, 2003 at 19:21 UTC
|
Re: So it isn't valid to take a reference to something that hasn't yet been defined
Actually, while working on Exporter::VA I discovered that it does in fact work. Taking the reference early defines a place-holder for it, and later defining it fills in that place holder. Taking a reference to the thing after it's been defined properly gives the same reference as taking such a reference "too early".
BEGIN { print \&baz, "\n" }
sub baz {}
print \&baz;
What you can't do is call baz from the begin block. But you can certainly stash away a reference to it for later.
I do exactly that in Exporter::VA. The import function can see a ref as a parameter, even though the implicit BEGIN block is causing this to run before the thing exists. To make this work well, at import() time it only remembers the reference, and does not try to update the contents (its changes will be clobbered by your initialization later) or rely on it being set-up just yet. Later, a function that import() put into my package is executed, and it can refer to that reference now.
So, you need to make your use statement just set-up, and "commit" the action later. In this case, sticking an END block into the calling package might do the trick.
—John | [reply] [d/l] [select] |
Re: Code references as use() parameters
by Jenda (Abbot) on Apr 09, 2003 at 22:28 UTC
|
Are you going to need the procedure anymore or is it just for this use statement? If it's just to the use you can do it like this:
use Alzabo::MethodMaker(
schema => 'greenpartydb',
class_root => 'GreenPartyDB::Database',
all => 1,
name_maker => sub {
....
}
);
Jenda
Always code as if the guy who ends up maintaining your code
will be a violent psychopath who knows where you live.
-- Rick Osborne
Edit by castaway: Closed small tag in signature | [reply] [d/l] [select] |
Re: Code references as use() parameters
by BrowserUk (Patriarch) on Apr 09, 2003 at 16:50 UTC
|
Wouldn't a simple pre-declaration in a BEGIN block solve the problem?
You could even put this with or inside the subroutine body to keep things together.
#! perl -slw
use Try qw[ \&callback ];
sub callback{
BEGIN{ sub callback; }
#do stuff
}
Examine what is said, not who speaks.
1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
3) Any sufficiently advanced technology is indistinguishable from magic.
Arthur C. Clarke.
| [reply] [d/l] |
|
|
Wouldn't a simple pre-declaration in a BEGIN block solve the problem?
Nope as the sub still has to be parsed e.g
BEGIN {
foo();
}
sub foo {
BEGIN { sub foo }
print "in foo()";
}
__output__
Undefined subroutine &main::foo called at 249927.pl line 2.
BEGIN failed--compilation aborted at 249927.pl line 3.
So you need to delay the execution of the coderef until after compile-time unless you can force the compilation of the given sub.
HTH
_________ broquaint | [reply] [d/l] |
|
|
| [reply] |