IDDQD
IDDQD

Reputation: 3741

Perl: struct-like storage of data

I have an existing perl script which I have to modify. For this, I need some struct-like container for my data. I do not have any 'outside' modules, nor do I possess the capability to obtain them, and my perl is 5.8.8. I've written a package containing one var and two arrays for my needs, however I cannot get it to work and I am not sure why. Here it is:

{
    package TestData;

    sub new
    {
        my $class = shift;
        my $self = {
          _id          => shift,
          _genUsers    => [],
          _testSymbols => [],
        };
        return bless ($self, $class);
    }

    sub setId
    {
        my ($self, $id) = @_;
        $self->{_id} = $id if defined($id);
    }

    sub addGenUser
    {
        my ($self, $user) = @_;
        push @{$self->{_genUsers}}, $user;
    }

    sub addTestSymbol
    {
        my ($self, $sym) = @_;
        push @{$self->{_testSymbols}}, $sym;
    }

    sub getId
    {
        my $self = @_;
        return $self->{_id};
    }

    sub getGenUserList
    {
        my $self = @_;
        return @{$self->{_genUsers}};
    }

    sub getTestSymbolList
    {
        my $self = @_;
        return @{$self->{_testSymbols}};
    }
}

use strict;
use POSIX;

my $id = "test";

my @a;
push @a, "this";

my @b;
push @b, "that";


my $obj = new TestData($id, @a, @b);
print "DEBUG: " . $obj->getId() . "\n";

Last line always throws "use of uninitialized value". What's going on?

Also would it be possible to do something like this:

my @c;
push @c, $obj;
foreach(@c){
    print "DEBUG2: " . $_->getId() . "\n";
}

Thank you.

EDIT: Thank you everyone for your replies. This is what the fully working end result looked like:

{
    use strict;
    use POSIX;

    package TestData;

    sub new
    {
        my $class = shift;
        my $self = {
          _alpha        => shift,
          _beta         => shift,
          _gamma        => shift,
          _delta        => shift,
          _theta        => shift
        };
        return bless ($self, $class);
    }

    sub getAlpha
    {
        my $self = shift;
        return $self->{_alpha};
    }

    sub getBeta
    {
        my $self = shift;
        return $self->{_beta};
    }

    sub getGamma
    {
        my $self = shift;
        return $self->{_gamma};
    }

    sub getDelta
    {
        my $self = shift;
        return $self->{_delta};
    }

    sub getTheta
    {
        my $self = shift;
        return $self->{_theta};
    }
}

Upvotes: 0

Views: 116

Answers (3)

Ilmari Karonen
Ilmari Karonen

Reputation: 50338

The bug is in the line(s) that read:

my $self = @_;

This is a scalar assignment, and so assigns the length on @_ to $self. Trying to use the length as a hash reference then gives the "use of uninitialized value" warning you see, and returns undef.

By the way, in addition to enabling warnings, you should get into the habit of starting all your scripts and modules with use strict;. If you'd done that, it would've caught this bug, and you would've received a runtime error saying something like:

Can't use string ("1") as a HASH ref while "strict refs" in use at foo.pl line 36.

Anyway, to fix your code, you should replace the line above with either:

my ($self) = @_;

or:

my $self = shift;

or even:

my $self = $_[0];

With my ($self) = @_ the parentheses turn it into a list assignment; with my $self = shift it's still a scalar assignment, but shift (which, inside a sub, is shorthand for shift @_) pulls the first scalar out of @_.

The choice of which style to prefer basically comes down to personal taste and consistency; if you're used to pulling all your method args out of @_ with a single assignment like:

my ($self, $foo, $bar, $baz) = @_;

then, for methods that take no arguments other than $self, a single-element list assignment seems more consistent. On the other hand, if you prefer to pick your args out of @_ one at a time, as in:

my $self = shift;
my $foo  = shift;
my $bar  = shift;
my $baz  = shift;

the you obviously should follow that pattern for single-arg methods too.

Upvotes: 2

salparadise
salparadise

Reputation: 5805

Change

my $self = @_;

to

my $self = shift;

in your methods that only expect one argument and especifically the called one (i.e.):

  sub getId
    {
        my $self = shift;
        return $self->{_id};
    }

You can also wrap the $self around parentheses to change context and do the same thing as commented

Output:

DEBUG: test

Upvotes: 2

Diab Jerius
Diab Jerius

Reputation: 2320

Rather than roll your own, you might want to check out Class::Struct, which has been part of Perl's core since 5.4.

Upvotes: 1

Related Questions