hwnd
hwnd

Reputation: 70732

Unless constructor argument passed is a hash type, croak on invalid arguments?

I am vaguely confused a bit on different methods of passing certain arguments to the constructor type. I want to only pass a hash reference \%hash, or a list foo => 1, bar => 1 but not both and croak if anything else is passed i.e ( single elements, array reference ).

For example, I pass my reference or list.. (This works for the way I do this)

my $obj = foo->new;
my $data = $obj->dump( \%hash );
my $data = $obj->dump( foo => 1, bar => 1 );

or

my $obj = foo->dump( \%hash );
my $obj = foo->dump( foo => 1, bar => 1 );

Package module:

package foo;

use strict;
use Carp;

use Scalar::Util qw/reftype/;

sub new { return bless {}, shift }

sub dump {
   my $class = shift;
   my $self  = shift;
   unless ( reftype( $self ) eq reftype {} ) {
         croak("Constructor method not a hash type!");
   }
}

1;

I've also thought about using the conditional operator ? : here, but I can't get it to error properly.

my $self  = reftype($_[0]) eq reftype {} ? shift : {@_};

Is there a better preferred way to do this?

Upvotes: 1

Views: 418

Answers (2)

ikegami
ikegami

Reputation: 386331

There's no such thing as a "hash list". foo => 1, bar => 1, is just a four element list. Sounds like you want to accept hash refs and even numbers of args.

sub dump {
   my $self = shift;
   my %args;
   if (@_ == 1) {
      croak("...") if (ref($_[0]) // '') ne 'HASH';
      %args = %{ $_[0] };
   } else {
      croak("...") if @_ % 2 != 0;
      %args = @_;
   }

   ...
}

Upvotes: 2

amon
amon

Reputation: 57640

We can look at the various ways your dump method can be called.

If we pass a "hash list", the number of elements is even (@_ % 2 == 0). Also, if at least one key-value pair is present, the first argument (a key) is a string, so not defined reftype $_[0] holds.

If we pass a hash reference, then the argument list should only hold this reference, and no other values: @_ == 1. The first argument will be a hash: reftype($_[0]) eq 'HASH'.

So to put the arguments in a hash reference, one could do something like:

sub dump {
  my $invocant = shift;
  my $hashref;
  if (@_ == 1 and reftype $_[0] eq 'HASH') {
    $hashref = $_[0];
  } elsif (@_ % 2 == 0 and (@_ == 0 or not defined reftype $_[0])) {
    $hashref = +{ @_ };
  } else {
    croak "Unknown argument format: either pass a hashref, or an even-valued list";
  }
  ...; # do something with $hashref
}

To find out if the $invocant is the class name or an object, just ask it if it is blessed:

if (defined Scalar::Util::blessed $invocant) {
  say "Yep, it is an object";
} else {
  say "Nope, it is a package name";
}

Upvotes: 2

Related Questions