You probably won't need to know this, but here it is just in case you do. I stumbled across it while trying to add some speedups to Math-GMP, which uncovered a bug in Test-Builder; a cpangrep then found 5 other distributions trying to do the same thing, all of which also had the same bug (as well as a few that simply bundle Test::Builder).
If you use overload to make your objects behave specially - as Math::GMP does, to make the objects act more like perl's numeric scalars - you provide those behaviours by way of a hash keyed on the (occasionally cryptic) name of the behaviour, providing a coderef or a function name:
package My::SuperNumber; use overload ( # overload the subtraction operator '-' => sub { my($self, $other, $swap) = @_; my $result = $self->my_super_subtract($other); return $swap ? -$result : $result; }, );
That $swap is there to handle asymmetric operations like subtraction: if the caller asks for $super - 12 perl will call that coderef with ($super, 12, 0) as parameters, but if they ask for 34 - $super perl will instead pass ($super, 34, 1) to say that the parameters have been swapped.
Now for simplicity perl always calls these overload functions the same way, even for unary operators:
use overload ( # this is the cryptic name of the "numification" operator '0+' => sub { my($self, $other, $swap) = @_; return $self->as_number; }, );
$other and $swap are still passed in when calling this method (as undef and 0), but of course everyone ignores them and writes the function above as if it took only one parameter. And that's all fine until you want to use XS to provide the overloaded method - at the C level you can't just ignore arguments you don't care about, you have to supply a signature that matches how it is called. So you have to write something like this:
use overload ( # this is the cryptic name of the "stringification" operator '""' => op_stringify, ); # and then in the XS code: char * op_stringify(left, right, swap) SV * left SV * right bool swap CODE: RETVAL = my_stringify(left); OUTPUT: RETVAL
.. and that's all fine too - XS writers expect to have to do things in slightly more convoluted ways. However, back in the land of Perl, the overload module also provides a Method function which allows you to ask for the coderef that _would_ be called for a particular overloaded operation. You might use that to check if it's safe to use something as a string, for example:
sub as_string { my($obj) = @_; return "$obj" unless ref $obj; die "Give me a string, or something that pretends to be one" unless overload::Method($obj, '""'); return "$obj"; }
However you might also use it to invoke that method yourself, and this is the thing that everyone got wrong:
sub as_string { my($obj) = @_; return "$obj" unless ref $obj; my $method = overload::Method($obj, '""') or die "Give me a string, or something that pretends to be one"; # WRONG return $method->($obj); # RIGHT return $method->($obj, undef, 0); }
If you invoke the coderef returned by overload::Method without supplying 3 arguments, that may work fine as long as you only interact with objects whose overloads are provided by perl code, but trying to invoke an XS method that way will fall over at runtime with a message like Usage: My::SuperNumber::as_string(left, right, swap) - because they _had_ to write it with that signature to let perl call it. And since use of XS is fairly rare and use of overload is even more so, it may be a long time before you discover there's a bug. So don't do that. :)
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Calling an overload::Method
by syphilis (Archbishop) on Feb 04, 2022 at 11:36 UTC | |
|
Re: Calling an overload::Method
by etj (Priest) on Jun 05, 2024 at 19:54 UTC | |
|
Re: Calling an overload::Method
by etj (Priest) on Mar 23, 2022 at 14:49 UTC | |
by hv (Prior) on Mar 24, 2022 at 12:30 UTC | |
by etj (Priest) on Mar 24, 2022 at 15:13 UTC |