[comp.lang.perl] passing by reference

alan@sbi.sbi.com (Alan Zeigler) (06/20/91)

I was trying to put together a package to simplify manipulation
of arrays of filehandles, arrays of arrays, etc. and am still
confused on a few points.  In the example in the shar below,
I use a "heap" as a source of variable references which works
fine;  however, when I try to free all possible references to
the variables, I have some problems:

 1. Is there a simple way to free all references to a
    symbol, say `foo', rather than going through and
    saying:

	undef($foo);
	undef(@foo);
	...

    reset() dosen't seem to do what I want.

 2. Even if I do it the brute force way, I seem to develop a
    memory leak if I include:

	undef(&foo);


I also have a more generic pass-by-reference question. When I know
I'm going to be playing with *foo, I was tempted to stick a

    local(*foo);

at the beginning of the block, but that seemed to result in
a corrupted heap as the block was exited.  Am I missing something?

Thanks in advance,
Alan Zeigler

P.S.: SunOS 4.1.1, PERL 4.010

#!/bin/sh
#
#	Run the following text with /bin/sh to create:
#	  heap/heap.pl
#	  heap/heaptest
#	  heap/foo
#	  heap/bar
#
if test -f heap; then echo "File, not directory, heap exists"; else
if test -d heap; then :; else mkdir heap; fi
if test -f heap/heap.pl; then echo "File heap/heap.pl exists"; else
echo "x - extracting heap/heap.pl (Text)"
sed 's/^X//' << 'SHAR_EOF' > heap/heap.pl &&
X;#=============================================================================
X;#| @(#)heap.pl	1.1	91/06/20	SB
X;#|
X;#| PERL "Heap" Package
X;#|
X;#|	A heap is a named set of variable references appropriate for
X;#|	manipulation in arrays of filehandles, arrays of arrays, etc.
X;#|	The entire set of references can be freed by name.
X;#|
X;#| Created 91/06/20, Alan M Zeigler, uunet!sbi!alan
X;#| Arbitrage Trading/Proprietary Analytics, Salomon Brothers Asia, Tokyo
X;#=============================================================================
X
Xpackage heap;
X
X;#-----------------------------------------------------------------------------
X;#	&heap_new($heap_name)
X;#
X;#	Returns a new reference into the named heap.
X;#-----------------------------------------------------------------------------
Xsub main'heap_new {
X    local($heap_name) = @_;
X
X    eval('*' . $heap_name . eval('++$' . $heap_name));
X}
X
X;#-----------------------------------------------------------------------------
X;#	&heap_reset($heap_name)
X;#
X;#	Free all references in the named heap.
X;#-----------------------------------------------------------------------------
Xsub main'heap_reset {
X    local($heap_name)	= @_;
X
X    local(*cnt)		= eval('*' . $heap_name);
X    local($heap_cnt);
X
X    for (; $cnt; --$cnt) {
X	$heap_cnt = $heap_name . $cnt;
X
X	#	IS THERE A BETTER WAY TO FREE ALL REFERENTS?
X	eval(<<"        EOF");
X	    close($heap_cnt);
X	    closedir($heap_cnt);
X	    dbmclose($heap_cnt);
X	    undef(\$$heap_cnt);
X	    undef(\@$heap_cnt);
X	    undef(\%$heap_cnt);
X	    #	    ADDING THIS INTRODUCES A MEMORY LEAK!
X	    #undef(&$heap_cnt);
X        EOF
X    }
X
X    eval('undef($' . $heap_name . ')');
X}
X
X1;
SHAR_EOF
chmod 0444 heap/heap.pl || echo "restore of heap/heap.pl fails"
set `wc -c heap/heap.pl`;Sum=$1
if test "$Sum" != "1670"
then echo original size 1670, current size $Sum;fi
fi
if test -f heap/heaptest; then echo "File heap/heaptest exists"; else
echo "x - extracting heap/heaptest (Text)"
sed 's/^X//' << 'SHAR_EOF' > heap/heaptest &&
X#!/usr/local/bin/perl -P
X;#=============================================================================
X;#| @(#)heaptest	1.1	91/06/20	SB
X;#|
X;#| PERL "Heap" Test
X;#|
X;#| Created 91/06/20, Alan M Zeigler, uunet!sbi!alan
X;#| Arbitrage Trading/Proprietary Analytics, Salomon Brothers Asia, Tokyo
X;#=============================================================================
X
Xrequire('heap.pl');
X
X;#	Invoke as `perl heaptest', disabling cpp, to run silent
X;#	and repetitively ... checking for leaks.
X#if 0
Xclose(STDOUT);
Xwhile ($i++ < 1000000) {
X#endif
X
X    #--------------------------------------------------------------------------
X    #	    Heap used for array of filehandles.
X    #--------------------------------------------------------------------------
X
X    undef(@filehandle_array);
X
X    #	    Open array of filehandles.
X    foreach ('foo', 'bar') {
X	*FILEHANDLE = &heap_new('filehandle_heap');
X	open(FILEHANDLE, $_);
X	push(@filehandle_array, *FILEHANDLE);
X    }
X
X    #	    Interleave lines of files.
X    do {
X	$more = 0;
X	foreach $filehandlep (@filehandle_array) {
X	    *FILEHANDLE = $filehandlep;
X	    ($_ = <FILEHANDLE>) && ++$more && print;
X	}
X    } while ($more);
X
X    #	    Free the heap (which closes files).
X    &heap_reset('filehandle_heap');
X
X
X    #--------------------------------------------------------------------------
X    #	    Heap used for array of arrays (nested arrays).
X    #--------------------------------------------------------------------------
X
X    #	    Even top level array is in heap (as @filehandle_array
X    #	    could have been in the previous example).
X    *array_of_arrays = &heap_new('array_of_arrays_heap');
X
X    #	    Allocate and populate nested arrays.
X    foreach (1 .. 10) {
X	*array = &heap_new('array_of_arrays_heap');
X	@array = $_ .. $_ + 9;
X	push(@array_of_arrays, *array);
X    }
X
X    #	    Verify nested arrays.
X    foreach (@array_of_arrays) {
X	*array = $_;
X	print(join(' ', @array), "\n");
X    }
X
X    #	    Free nested array.
X    &heap_reset('array_of_arrays_heap');
X
X
X;#	End of leak-test loop.
X#if 0
X}
X#endif
SHAR_EOF
chmod 0555 heap/heaptest || echo "restore of heap/heaptest fails"
set `wc -c heap/heaptest`;Sum=$1
if test "$Sum" != "2066"
then echo original size 2066, current size $Sum;fi
fi
if test -f heap/foo; then echo "File heap/foo exists"; else
echo "x - extracting heap/foo (Text)"
sed 's/^X//' << 'SHAR_EOF' > heap/foo &&
Xfoo1
Xfoo2
Xfoo3
Xfoo4
Xfoo5
Xfoo6
Xfoo7
Xfoo8
Xfoo9
SHAR_EOF
chmod 0644 heap/foo || echo "restore of heap/foo fails"
set `wc -c heap/foo`;Sum=$1
if test "$Sum" != "45"
then echo original size 45, current size $Sum;fi
fi
if test -f heap/bar; then echo "File heap/bar exists"; else
echo "x - extracting heap/bar (Text)"
sed 's/^X//' << 'SHAR_EOF' > heap/bar &&
Xbar1
Xbar2
Xbar3
Xbar4
Xbar5
Xbar6
Xbar7
Xbar8
Xbar9
SHAR_EOF
chmod 0644 heap/bar || echo "restore of heap/bar fails"
set `wc -c heap/bar`;Sum=$1
if test "$Sum" != "45"
then echo original size 45, current size $Sum;fi
fi
fi
exit 0
-- 
 Alan M Zeigler
 Arbitrage Trading/Proprietary Analytics, Salomon Brothers Asia Limited
 Urbannet Otemachi Bldg, 2-2, Otemachi 2-chome, Chiyoda-ku, Tokyo 100, Japan
 Work: 81 3 5255-4483 | FAX: 81 3 5255-5598 | E-Mail: uunet!sbi!alan

tchrist@convex.COM (Tom Christiansen) (06/20/91)

From the keyboard of alan@sbi.sbi.com (Alan Zeigler):

>while ($i++ < 1000000) {
>    foreach ('foo', 'bar') {
>	*FILEHANDLE = &heap_new('filehandle_heap');

I'm not entirely surprised you're getting a leak here.  From the man page:

     Assignment to *name is currently recommended only inside a
     local().  You can actually assign to *name anywhere, but the
     previous referent of *name may be stranded forever.  This
     may or may not bother you.

I suspect that's what happening to you.

Of course, if you say local(*FILEHANDLE) in your loop, you'll still
get 1000000 versions pushed on the stack, which I'd call sub-optimal.

--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
		"So much mail, so little time."