stephen.baldwin has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks:

I have a strange problem. If anyone can explain what is going wrong with the code below, please give me a shout.

I know there are multiple ways to skin the cat so-to-speak so I don't need "why don't you do it this way instead" answers -- just what is wrong with the edited code below.

Context:

This is an involved way of building a directory and file structure from a variety of sources, including a ZIP file. The ZIP member contents should be appended to an existing version of that file if found.

1. Basically I am extracting a member from the ZIP file fine.

2. I am adding the member ($member) to an array (@srcArr) and passing the array reference \@ through to a subroutine (CopyFile()). All fine

3. CopyFile: I am extracting the ZIP member and for debug printing the filename and the contents. This all works well.

4. CopyFile: The member is pushed onto another array @appendArr for later processing.

5. CopyFile: When the member is popped off the @appendArr array, nothing works (member->fileName() etc.)

ERROR Can't locate object method "fileName" via package "Archive::Zip::ZipFileMember=HASH(0x3d99cdc)" (perhaps you forgot to load "Archive::Zip::ZipFileMember=HASH(0x3d99cdc)"?) at C:\Users\e5313240\Documents\FIS\MB\Perl\Assignment.pl line 2555.

Here is the relvant code segment :

my $member = $appendMemberArr[2]; 2555 print "member->fileName=".$member->fileName()."\n";

See rest of code here:

use Archive::Zip; my $fZipIn = Archive::Zip->new($fSource) or die "### ERROR $thisFunc(" +.__LINE__."): FAILED TO CREATE ZIP FOR $fSource\n"; my @zMemberArr = $fZipIn->members(); # loop here for processing each member [stored in $member] push @srcArr, ($member); CopyFile(\@idArr,\@cmdArr,\@srcArr,\@destArr,[]); } sub CopyFile # the bit we like is $_[2] { my @srcArr = @{$_[2]}; # looping through each @srcArr - counter is $c print $gfLog "append found $srcArr[$c] ".$srcArr[$c]->fileName()."\n +"; if ($idArr[$c] eq "ZIP") { print "contents>>>\n".$srcArr[$c]->contents()."\n<<<contents\n"; # + <=== BUT THIS WORKS FINE } # adding it to the appendArr for later processing push @appendArr,("$idArr[$c],$command,$srcArr[$c],$destArr[$c],$cmdA +rgs"); # lots of processing later, we want to process our appendArr # 0 is type # 1 is command # 2 is source # 3 is dest # 4 is file commands my $fAppend; foreach $fAppend (@appendArr) { print $gfLog "fAppend=$fAppend\n"; (my @appendMemberArr) = split(/,/,$fAppend); print $gfLog "$thisFunc: Processing append: $appendMemberArr[0] $a +ppendMemberArr[2]\n"; mkpath(dirname $appendMemberArr[3]) if (! -d dirname($appendMember +Arr[3])); if ($appendMemberArr[0] eq "ZIP") { my $member = $appendMemberArr[2]; print "member->fileName=".$member->fileName()."\n"; # <=== THIS +IS THE ERROR # other processing } }

Replies are listed 'Best First'.
Re: Archive::Zip: Passing members to subroutines
by Corion (Patriarch) on Jan 31, 2018 at 21:28 UTC
    ERROR Can't locate object method "fileName" via package "Archive::Zip::ZipFileMember=HASH(0x3d99cdc)"

    Somewhere in your code, you converted the reference to 0x3d99cdc to a string, and now you're trying to call methods on this string. That doesn't work.

    I think this happens here:

    push @appendArr,("$idArr[$c],$command,$srcArr[$c],$destArr[$c],$cmdArg +s");

    If instead of converting all those nice references to a string you use an array, you keep the references as references:

    push @appendArr,[$idArr[$c],$command,$srcArr[$c],$destArr[$c],$cmdArgs +];

    Later, don't split that string, but use it as array immediately:

    # (my @appendMemberArr) = split(/,/,$fAppend); (my @appendMemberArr) = @$fAppend;
      Hello Corion: Thank you so much for your prompt reply. You were of course 100% correct! That was the offending line. In the end I opted to keep everything in one array:
      push @appendArr,([$idArr[$c],$command,$srcArr[$c],$destArr[$c],$cmdArg +s]);
      and access it thus:
      foreach $fAppend (@appendArr) { print "is an array\n" if (ref($fAppend) eq "ARRAY"); print "fAppend[0]=@$fAppend[0]\n"; print "fAppend[2]=".(@$fAppend[2])->fileName()."\n"; print "fAppend[2]=".(@$fAppend[2])->contents()."\n"; ...
      If you have time and inclination, could you expound on what is going on with the referencing and typing?

      I presume the original ZIP "member" object is a hash reference.

      The array was originally passed through as a scalar \@ then plopped into a string.

      And as you pointed out, it lost its array-ness and hash-ness when I pushed it in a string "...,$member,..." into @appendArr. How does that happen?

      If you have any reading recommendations on these intricacies, I'd be pleased to receive them!

      Thanks again for your help!

      We are up and running.

      Rgds

      Steve

        Converting something to a string is mostly a one-way road.

        You can only interchangeably use a string as a number or a string. There is no (sane) way to convert a string back to a reference in Perl.

        The process of converting something to a string is called "stringification" in Perl. I'm not sure what you mean by "how does that happen" - I could explain to you how Perl constructs a string in which you use variables, or I could guess about how you wrote code that did that, but I'm not sure that any of these would give you the information you really seek.