Reputation: 394
When I redefine my own subroutine (and not a Perl built-in function), as below :
perl -ce 'sub a($$$){} sub b {a(@_)}'
I get this error :
Not enough arguments for main::a at -e line 1, near "@_)"
I'm wondering why.
Edit :
The word "redefine" is maybe not well chosen. But in my case (and I probably should have explained what I was trying to do originally), I want to redefine (and here "redefine" makes sense) the Test::More::is function by printing first Date and Time before the test result.
Here's what I've done :
Test::More.pm :
sub is ($$;$) {
my $tb = Test::More->builder;
return $tb->is_eq(@_);
}
MyModule.pm :
sub is ($$;$) {
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is(@_);
}
Upvotes: 0
Views: 474
Reputation: 386501
In Perl, prototypes don't validate arguments so much as alter parsing rules. $$;$
means the sub expects the caller to match is(EXPR, EXPR)
or is(EXPR, EXPR, EXPR)
.
In this case, bypassing the prototype is ideal.
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is(@_);
}
Since you don't care if Test::More::is
modifies yours @_
, the following is a simple optimization:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is;
}
If Test::More::is
uses caller
, you'll find the following useful:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
goto &Test::More::is;
}
Upvotes: 4
Reputation: 69314
The prototype that you have given your subroutine (copied from Test::More::is
) says that your subroutine requires two mandatory parameters and one optional one. Passing in a single array will not satisfy that prototype - it is seen as a single parameter which will be evaluated in scalar context.
The fix is to retrieve the two (or three) parameters passed to your subroutine and to pass them, individually, to Test::More::is
.
sub is ($$;$) {
my ($got, $expected, $test_name) = @_;
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is($got, $expected, $test_name);
}
The problem has nothing to do with your use of a prototype or the fact that you are redefining a subroutine (which, strictly, you aren't as the two subroutines are in different packages) but it's because Test::More::is()
has a prototype.
Upvotes: 7
Reputation: 54371
You are not redefining anything here.
You've set a prototype for your sub a
by saying sub a($$$)
. The dollar signs in the function definition tell Perl that this sub has exactly three scalar parameters. When you call it with a(@_)
, Perl doesn't know how many elements will be in that list, thus it doesn't know how many arguments the call will have, and fails at compile time.
Don't mess with prototypes. You probably don't need them.
Instead, if you know your sub will need three arguments, explicitly grab them where you call it.
sub a($$$) {
...
}
sub b {
my ($one, $two, $three) = @_;
a($one, $two, $three);
}
Or better, don't use the prototype at all.
Also, a
and b
are terrible names. Don't use them.
Upvotes: 5