Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello,
I have this snippet:

use strict; use lib ("./Lib"); use Datum; my $datum = new Datum;
and, in ./Lib/Datum.pm
package Datum; use strict; use warnings; use Data::Dumper; use Class::Struct; struct Datum => { heights_agl => '@', }; sub heights_agl { my $self = shift; if (@_) { $self->set_values(Variable=>'heights_agl',@_); } } sub set_values { my $self = shift; my %args = @_; die Dumper \@_; } 1;
This gives me an error when run with Perl versions after 5.16.
It seems to me that after V 5.16 the override function is called when creating the Class, thus giving the error, while it is not before V5.16

With Perl V5.16:

C:\Tmp\debug.4>perl driver.pl function 'heights_agl' already defined, overrides struct accessor meth +od at Lib/Datum.pm line 8. C:\Tmp\debug.4>perl -v This is perl 5, version 16, subversion 3 (v5.16.3) built for MSWin32-x +64-multi-thread
And with Perl V5.26:

$ perl driver.pl function 'heights_agl' already defined, overrides struct accessor meth +od at Lib/Datum.pm line 8. Odd number of elements in hash assignment at Lib/Datum.pm line 25. $VAR1 = [ 'Variable', 'heights_agl', [] ]; $ perl -v This is perl 5, version 26, subversion 1 (v5.26.1) built for x86_64-li +nux-gnu-thread-multi (with 67 registered patches, see perl -V for more detail)
Is this hypothesis correct and is there a way to have it working again?
Thank you

Replies are listed 'Best First'.
Re: Class::Struct accessor function after Perl V5.16 (updated)
by haukex (Archbishop) on Jan 02, 2019 at 11:11 UTC
    This gives me an error when run with Perl versions after 5.16.

    Well technically, you're the one causing the error ;-) die Dumper \@_;

    I'm pretty sure the difference in behavior you're seeing is because bug #29230 was fixed in Class::Struct version 0.64, which was released with v5.18.0. It appears that the constructor is supposed to initialize the fields <update> using the custom accessors </update>.

    In sub set_values, you expect there to be an even number of arguments that you assign to a hash (my %args = @_;), but in sub heights_agl, you do ->set_values(Variable=>'heights_agl',@_), which means that if @_ contains an odd number of elements, you'll get the "Odd number of elements in hash assignment" warning (not a fatal error, but a useful warning).

    I'm pretty sure this would happen any time you assign <update> an array ref </update> to heights_agl - so I think you need to rethink your logic anyway. Since you haven't shown what set_values is actually doing, it's a little hard to tell what the best solution would be, but perhaps you might want to change the call to something like $self->set_values(Variable=>'heights_agl',Arguments=>[@_]);?

    Update: Note the documentation of Class::Struct:

    Array ('@' or '*@')

    The element is an array, initialized by default to ().

    ... As a special case, when the accessor is called with an array reference as the sole argument, this causes an assignment of the whole array element. The object reference is returned.

    So I guess you'll need to extend your set_values to handle this case.

Re: Class::Struct accessor function after Perl V5.16
by tobyink (Canon) on Jan 02, 2019 at 11:31 UTC

    Doing a diff on the Class::Struct included with Perl 5.14 (I don't have 5.16 on my system) and 5.26, it does look like the constructor was changed from doing direct assignment of initial values into the blessed hashref to instead calling the object's accessors to assign initial values. It doesn't look like there's any way to have this work consistently between the versions, short of writing your own constructor, and then you miss out on most of the benefits of using Class::Struct to begin with.

    Personally, I'd recommend switching to using one of the better supported OO toolkits on CPAN: Class::Tiny, Moo, or Moose.

Re: Class::Struct accessor function after Perl V5.16
by Anonymous Monk on Jan 02, 2019 at 12:15 UTC
    Thanks for you comments.
    So I think I can fix it by checking parameters in set_values with
    if (@_&& ref @_ ne ‘ARRAY’) {
      if (@_&& ref @_ ne ‘ARRAY’) {

      No, not quite, ref @_ won't do what you want. If you wanted to check that a function was called with exactly one argument that is an arrayref: if ( @_==1 && ref $_[0] eq 'ARRAY' ) { ... }

        Sorry, I forgot to mention that set_heights, apart from initialization stage, is called passing a list of key/value pairs.
        So
        if (@_ && ref $_[0] ne 'ARRAY') {
        is now fixing the code. Thanks a lot for your time and help.