develooper Front page | perl.beginners | Postings from June 2023

Re: type checks

Thread Previous | Thread Next
From:
sisyphus
Date:
June 3, 2023 04:11
Subject:
Re: type checks
Message ID:
CADZSBj0a4fX06mrGCLgyfxmRhn4O_uAep=Zh53ooxYf-6t-ueg@mail.gmail.com
Just for fun, I've written the following which is essentially the way that
I would go about meeting the OP's requirements (as I understand them).
It's a file that needs to be named NumOnly.pm and, to be usable, it needs
to be in a location where it is found - eg in the "Math" folder of one of
the directories listed in perl's @INC.

Have a play with it if you're interested - and I'll answer questions as
best I can, as they arise.
It hasn't been rigorously tested, and may require further modification.
I've overloaded only +, -, *, /, <=>, and "" operations.
Other overloaded operations can easily be added. See 'perldoc overload' for
a full list of overloadable operations.
Note that there's a very basic demo at the bottom of the file that's
intended as a starter.

##############################################################
package Math::NumOnly;
use strict;
use warnings;
use B;

use overload
'+'    => \&oload_add,
'-'    => \&oload_sub,
'*'    => \&oload_mul,
'/'    => \&oload_div,
'<=>'   => \&oload_spaceship,
'""'   => \&oload_stringify,
;

sub new {
  shift if(!ref($_[0]) && $_[0] eq "Math::NumOnly"); # 'new' has been
called as a method

  my $ok = is_ok($_[0]);
  die "Bad argument (or no argument) given to new" unless $ok;

  if($ok == 1) {
    # return a copy of the given Math::NumOnly object
    my $ret = shift;
    return $ret;
  }

  # given arg must be a valid IV or NV
  my %h = ('val' => shift);
  return bless(\%h, 'Math::NumOnly');
}

  my %flags;
  {
    no strict 'refs';
    for my $flag (qw(
      SVf_IOK
      SVf_NOK
      SVf_POK
              )) {
      if (defined &{'B::'.$flag}) {
       $flags{$flag} = &{'B::'.$flag};
      }
    }
  }

sub oload_add {
  die "Wrong number of arguments given to oload_add()"
    if @_ > 3;

  my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
  die "Bad argument given to oload_add" unless $ok;

  if($ok == 1) {
    return Math::NumOnly->new($_[0]->{val} + $_[1]->{val});
  }

  return Math::NumOnly->new($_[0]->{val} + $_[1]);
}

sub oload_mul {
  die "Wrong number of arguments given to oload_mul()"
    if @_ > 3;

  my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
  die "Bad argument given to oload_mul" unless $ok;

  if($ok == 1) {
    return Math::NumOnly->new($_[0]->{val} * $_[1]->{val});
  }

  return Math::NumOnly->new($_[0]->{val} * $_[1]);
}

sub oload_sub {
  die "Wrong number of arguments given to oload_sub()"
    if @_ > 3;

  my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
  die "Bad argument given to oload_sub" unless $ok;

  my $third_arg = $_[2];

  if($ok == 1) {
    if($third_arg) {
      return Math::NumOnly->new($_[1]->{val} - $_[0]->{val});
    }
    return Math::NumOnly->new($_[0]->{val} - $_[1]->{val});
  }

  if($third_arg) {
    return Math::NumOnly->new($_[1] - $_[0]->{val});
  }
  return Math::NumOnly->new($_[0]->{val} - $_[1]);
}

sub oload_div {
  die "Wrong number of arguments given to oload_div()"
    if @_ > 3;

  my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
  die "Bad argument given to oload_div" unless $ok;

  my $third_arg = $_[2];

  if($ok == 1) {
    if($third_arg) {
      return Math::NumOnly->new($_[1]->{val} / $_[0]->{val});
    }
    return Math::NumOnly->new($_[0]->{val} / $_[1]->{val});
  }

  if($third_arg) {
    return Math::NumOnly->new($_[1] / $_[0]->{val});
  }
  return Math::NumOnly->new($_[0]->{val} / $_[1]);
}

sub oload_stringify {
  my $self = shift;
  return $self->{val};
}

sub oload_spaceship {
  die "Wrong number of arguments given to oload_spaceship()"
    if @_ > 3;

  my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
  die "Bad argument given to oload_spaceship" unless $ok;

  my $third_arg = $_[2];

  if($ok == 1) {
    if($third_arg) {
      return Math::NumOnly->new($_[1]->{val} <=> $_[0]->{val});
    }
    return Math::NumOnly->new($_[0]->{val} <=> $_[1]->{val});
  }

  if($third_arg) {
    return Math::NumOnly->new($_[1] <=> $_[0]->{val});
  }
  return Math::NumOnly->new($_[0]->{val} <=> $_[1]);
}


sub is_ok {
  return 0 unless defined $_[0];
  return 1 if ref($_[0]) =~ /Math::NumOnly/;
  my $flags = flags($_[0]);
  return 0 if $flags =~ /SVf_POK/;
  return 2 if $flags =~ /SVf_IOK/;
  return 3 if $flags =~ /SVf_NOK/;
  return 0;
}

sub flags {
  my $flags = B::svref_2object(\($_[0]))->FLAGS;
  join ' ', sort grep $flags & $flags{$_}, keys %flags;
}

1;

__END__

## Demo:

use strict;
use warnings;
use Math::NumOnly;

my $op = Math::NumOnly->new(123); # Won't accept anything that's not either
                                  # a number or another Math::NumOnly
object.
#$op += '29';                     # Would be a fatal error
$op += 29;                        # $op is now 152
print $op, "\n";
$op += Math::NumOnly->new(38);    # $op is now 190
print $op, "\n";
$op /= 6;                         # $op is now 31.666...67
print $op, "\n";
my $op2 = 6 * $op;                # $op2 is 190
print $op2, "\n";
print $op <=> $op, "\n";          # prints 0
print $op <=> $op2, "\n";         # prints -1
print $op2 <=> $op, "\n";         # prints  1
##############################################################

Cheers,
Rob

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About