1

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?

hwnd
  • 69,796
  • 4
  • 95
  • 132
  • You do realize that `new()` is not being called here? It's not directly relevant to your question, but if you call a variable in an object oriented Perl module `$self`, I think it should contain `$self` and not one of the explicit parameters. – innaM Jul 11 '13 at 15:48
  • Yes, I've just made it to where I could either use as so or `my $obj = foo->new; my $data = $obj->dump(..)` And what do you exactly mean by `contain $self and not one..`? – hwnd Jul 11 '13 at 15:54

2 Answers2

2

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";
}
amon
  • 57,091
  • 2
  • 89
  • 149
  • Why the `+` for `+{ @_ }`? – hwnd Jul 11 '13 at 16:08
  • 1
    The `+` is irrelevant in this case. (It is a habit I picked up to make it clear that the curly braces form a hashref. This is only important when you want `map` to return a hashref, e.g. `map +{ @$_ }, [1, 2], [3, 4]`). – amon Jul 11 '13 at 16:11
  • I have a related question to this question, instead of posting a new thread is their a way to chat? – hwnd Jul 12 '13 at 23:44
  • @JasonGray here: http://chat.stackoverflow.com/rooms/33396/chat-between-amon-and-jason-gray – amon Jul 12 '13 at 23:51
2

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 = @_;
   }

   ...
}
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Yes, originally I did something similar to this, but I see what I left out. Thanks for the feedback. – hwnd Jul 11 '13 at 16:05
  • What's the difference really between your code line and `..ne 'HASH';` and `ne ref {}`? – hwnd Jul 11 '13 at 18:14
  • `ref {}` needlessly creates and destroys two variables to get well known and constant string `HASH`. – ikegami Jul 11 '13 at 18:38