[p4perl] Help with Hashes

Tony Smith tony@smee.org
Wed, 29 Dec 2004 18:07:35 +0000


Craig Leigh wrote:

>Nice!
>
>This:
>
>	$p4->Edit ("c:/src/file.txt");
>	foreach my $e ($p4->Errors()){
>	      print "$e->[0]\n";
>	}
>
>gives:
>
>	Path 'c:/src/file.txt' is not under client's root 'C:\code'.
>
>Which is correct!
>
>But!
>
>	foreach $e (@$chglist){
>	print "$e->[0]\n";
>	}
>
>gives this:
>
>	Not an ARRAY reference at C:\src\p4synop.pl line 66, <STDIN>
>line 1.
>
>Huh!
>  
>
One of the most important things to know when working with P4Perl is 
what type of result to expect back  when you run a command. The rules 
are designed to make correct usage simple, but they're not always 
obvious. The comments above the implementation of P4::Run in P4.pm 
explain it best:

#
# Execute a command. The return value depends on the context of the call.
#
# Returns an array of results if the caller's asked for one
# Returns undef if result set is empty
# Returns a scalar result in scalar context if only one result exists.
# Returns an array ref in scalar context if more than one result exists.
#

Note that the return types above are evaluated in that order! i.e. if 
the caller's asked for an array, then an array is what they get and the 
rest is irrelevant.

When you run 'p4 edit //one/single/file' you can expect zero or one 
results (zero if there was an error). Whereas when you run 'p4 changes' 
you're usually going to get more than one result so you should expect an 
array. So, I'd typically use:

    my $output = $p4->Edit( "//one/single/file" );

and

    my @changes = $p4->Changes( '-t', '-m5' );

Basically the lesson is to give some consideration to the type of value 
you (a) want and (b) expect to be returned.

If you're searching for 100% consistency, the answer is to always call 
P4::Run in array context. e.g.

    my @changes = $p4->Changes( "-t", "-m5", "//depot/some/path/..." );

will always return an array no matter what the results look like.

In your specific case, you need to iterate over the result array and 
treat each member of the array as a hash ref (if you're using 
ParseForms() mode). i.e.

	foreach $c (@$chglist){
		printf( "Change: %d by %s\n", $c->{ 'change' }, $c->{ 'user' } );
	}


As Robert points out, you can also test the type of the return value for extra safety. YMMV though - it's easy to write more error checking code than code to do what you wanted in the first place.

Tony