|
syphilis has asked for the wisdom of the Perl Monks concerning the following question:
Hi,
I have a module A that overloads the '-' operator via its A::oload_minus() subroutine.
And I have a second module B that also overloads the '-' operator via it's own B::oload_minus() subroutine.
Both modules also have their own oload_add, oload_mul, oload_div, oload_mod and oload_pow subroutines that overload the other basic arithmetic operators).
I have constructed the overloading in module A to handle B objects.
But if module B's overloading subroutines are passed a module A object, it is (by my current design) a fatal error.
use A;
use B;
$A_obj = A->new(16);
$B_obj = B->new(6);
my $n1 = $A_obj - $B_obj; # $n1 is an A object with value 10
my $n2 = $B_obj - $A_obj; # Fatal error
In the above demo I want $n2 to be an A object, with the value of -10.
That is, I want the A::oload_minus() sub to receive the args ($A_obj, $B_obj, TRUE).
Instead, the B::oload_minus() sub is receiving the args ($B_obj, $A_obj, FALSE) - which is, by my current design, a fatal error since B::overload_minus() does not currently accept A objects.
Is there a way that I can work around this without making any changes to the B module ? (The motivation to not alter module B is simply that I don't want to add more clutter to B unless I need to.)
My module "A" is in fact Math::MPC, and my module "B" is in fact Math::MPFR.
AFTERTHOUGHT: I should point out that the arithmetic overloading in the publicly available versions of Math::MPC don't yet accept Math::MPFR objects. (I've currently implemented this new feature on my local Math::MPC builds only.)
Cheers, Rob
Re: A little overloading conundrum
by swl (Prior) on Mar 07, 2026 at 03:42 UTC
|
There's nothing I can think of and the documentation suggests it's not something that is easy to do.
One could reverse the order so $A_obj is checked first: my $n2 = -$A_obj + $B_obj;. That's obviously not practical, though, and would also need neg overloading on $A_obj (and addition if not already done).
| [reply] [d/l] [select] |
|
|
There's nothing I can think of and the documentation suggests it's not something that is easy to do.
Yes - the first point I read in that link to the documentation pretty much kills all hope:
1.If the first operand has declared a subroutine to overload the opera
+tor then use that implementation.
I suppose (untested) I could do:
my $n2 = (\$B_obj) - $A_obj;
and that would at least call module A's oload_minus() subroutine .... which would then be structured to de-reference the first argument and return the intended result.
But that solution is no more practical than the alternative you provided.
Thanks for the reply. I had, of course, consulted the overloading docs but had stopped reading before reaching the bit to which you linked.
It's not the end of the world if I have to modify module B.
Cheers, Rob | [reply] [d/l] [select] |
|
|
If you are working around it in the caller, this is clearer, if not as efficient:
my $n2 = -($A_obj - $B_obj);
| [reply] [d/l] |
|
|
Re: A little overloading conundrum
by choroba (Cardinal) on Mar 08, 2026 at 18:29 UTC
|
You can do ugly things to B even from within A:
#!/usr/bin/perl
use warnings;
use strict;
use experimental qw( signatures );
{ package My::A;
use overload '-' => \−
sub minus($x, $y, $swap) {
return ref($x)->new(
($y->isa('My::B') ? $x->[0] - $y->{value}
: ($x->[0] - $y->[0])
) * (1, -1)[$swap]
)
}
sub new($class, $value) {
bless [$value], $class
}
# We can change My::B outside of B, in fact.
eval q{ package My::B;
my $m = My::B->can('minus') or die 'My::B not loaded';
use overload "-" => sub ($x, $y, $s) {
return $y->isa('My::A') ? $y->minus($x, 1) : $m->($x,
+$y, $s)
}
};
}
{ package My::B; # Can't be changed!
use overload '-' => \−
sub minus($x, $y, $swap) {
my $subtr = $x->{value} - $y->{value};
return ref($x)->new($swap ? -$subtr : $subtr)
}
sub new($class, $value) {
bless {value => $value}, $class
}
}
my $a0 = 'My::A'->new(16);
my $a1 = 'My::A'->new(6);
my $b0 = 'My::B'->new(16);
my $b1 = 'My::B'->new(6);
use Data::Dumper;
print Dumper A => $a0 - $a1;
print Dumper B => $a0 - $b1;
print Dumper C => $b0 - $b1;
print Dumper D => $b0 - $a1;
map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
| [reply] [d/l] [select] |
|
|
You can do ugly things to B even from within A
Yes - and my overloading of the arithmetic operations are done in XSubs - which leads to a fair bit of code clutter, but is procedurally clean and simple (IMO, at least).
I actually don't fully understand the code you've provided, but I'll certainly try to remedy that when I get the chance. (I'm not really a proper perl programmer.)
Having decided that Math::MPC's basic arithmetic operators should be capable of handling Math::MPFR objects (since the MPC library already provides functions that perform such arithmetic), it's a fairly simple exercise to insert the few required lines of code into the relevant Math::MPC XSubs.
If not for this "conundrum", that would be the end of the exercise.
Math::MPFR, which has no business to engage in arithmetic with Math::MPC objects, could be left as is - with no need to even mention this new capability in the Math::MPFR docs.
The solution I use is simple enough, but it involves making changes to the Math::MPFR overloading XSubs.
It's only a matter of inserting one line of code into a new if(condition){} block in each of those XSubs. And it just does a callback to (eg) Math::MPC::overload_minus(mpc_object, mpfr_object, &PL_sv_yes) with the assistance of a 20-line macro.
I'm happy enough with that, and I've used that technique without issue in a number of cross-class overloading situations - though I have no idea how expensive that solution is.
There's even no need to check whether Math::MPC has been loaded. If a Math::MPC object has been passed to a Math::MPFR function then we know that perl has loaded Math::MPC.
It's just a bit annoying that I have to go that extra step.
It would be much better if I could simply tell perl's overloading process "hey, in this case, use the overloading subroutine of the second object". But ... I can't do that :-((
Cheers, Rob
| [reply] [d/l] [select] |
|
|
I actually don't fully understand the code you've provided, but I'll certainly try to remedy that when I get the chance.
First up, I wanted to remove the "experimental" requirement - assuming it's not a crucial piece of the demo. (I'm not at all familiar with experimental stuff, and I don't have much interest in it.)
I've also added some dumps of some more subtractions.
So, I've arrived at this modified version of choroba's script :
use warnings;
use strict;
#use experimental qw( signatures );
{ package My::A;
use overload '-' => \−
# sub minus($x, $y, $swap) { ############ Replaced by next line ##
sub minus { my($x, $y, $swap) = (shift, shift, shift);
return ref($x)->new(
($y->isa('My::B') ? $x->[0] - $y->{value}
: ($x->[0] - $y->[0])
) * (1, -1)[$swap]
)
}
# sub new($class, $value) { ############ Replaced by next line ##
sub new { my($class, $value) = (shift, shift);
bless [$value], $class
}
# We can change My::B outside of B, in fact.
eval q{ package My::B;
my $m = My::B->can('minus') or die 'My::B not loaded';
use overload "-" => sub { my($x, $y, $s) = (shift, shift,
+ shift);
return $y->isa('My::A') ? $y->minus($x, 1) : $m->($x,
+$y, $s)
}
};
}
{ package My::B; # Can't be changed!
use overload '-' => \−
# sub minus($x, $y, $swap) { ############ Replaced by next line ##
sub minus { my($x, $y, $swap) = (shift, shift, shift);
my $subtr = $x->{value} - $y->{value};
return ref($x)->new($swap ? -$subtr : $subtr)
}
# sub new($class, $value) { ############ Replaced by next line ##
sub new { my($class, $value) = (shift, shift);
bless {value => $value}, $class
}
}
my $a0 = 'My::A'->new(16);
my $a1 = 'My::A'->new(6);
my $b0 = 'My::B'->new(16);
my $b1 = 'My::B'->new(6);
use Data::Dumper;
print Dumper A => $a0 - $a1;
print Dumper B => $a0 - $b1;
print Dumper C => $b0 - $b1;
print Dumper D => $b0 - $a1;
print Dumper AA => $a1 - $a0;
print Dumper BB => $b1 - $a0;
print Dumper CC => $b1 - $b0;
print Dumper DD => $a1 - $b0;
At this point, I have 2 questions:
1. Where is can documented ?
2. How is it that I can use isa even though no features have been enabled ?
Regarding the first question:
>perldoc -f can
No documentation for perl function 'can' found
WRT the second question, the perlop documentation says this regarding isa:
This feature is available from Perl 5.31.6 onwards when enabled by
+ "use
feature 'isa'". This feature is enabled automatically by a "use v5
+.36"
(or higher) declaration in the current scope.
I have done neither of those things. Has perl-5.42.0 automatically called that feature for me ? ... or does it automatically call "use v5.36" ?
Cheers, Rob | [reply] [d/l] [select] |
|
|
|
|
|
|
|