|laziness, impatience, and hubris|
A warning about overloading assignment methodsby diotalevi (Canon)
|on Apr 20, 2005 at 06:07 UTC||Need Help??|
In an overloaded assignment method like -=, it is an error for most code to take a reference to $_ and include it in the result. Most often, this code should copy $_ into a temporary variable like $self and reference that instead. The rest of this note explains the problem that occurs when you use \$_.
The proper way to take a reference to $_ and include it in your result object.
In a -= method I wanted to take a reference to the current object and incorporate it into the new value. The normal way to do this is to take a reference to it and return a new object.
My expectation is that I would get an object containing a reference to another object. That expectation was violated when I got an object with a reference to itself. Due to some magic on the part of overload or the C code in perl implementing overloading, \ $_ in the return value is modified into a circular or self-reference.
Expected result$result = bless( [ \ ... ], "..." );
Actual result showing the self reference$result = bless( [ \ $result ], "...";
The fix was to reference the $_ value under a different name. I opted to use normal object code and said $self = shift so $_ would now be in $self. After doing this, I could take references to that value without having perl modify my result into a self-reference.
To debug the original behaviour, I created my correct data structure in my subtract() method, dumped it with Data::Dump::Streamer, returned it, and then dumped it again immediately outside. The observed behaviour was some "magical" alteration was occuring to my data between the time the return() happened and the time the value appeared as a value to be assigned.
I went so far as to run this code under a debugging perl to watch all the opcodes being executed. No additional code was being run and thus no perl code had the opportunity to alter my data. I attempted to debug this under gdb but due to my lack of skill wasn't able to verify that the returned value in (SV*)res in Perl_amagic_call was as I expected it.
Note that I checked that the object I was about to return was some ARRAY(0x12345) and my returned value was also the same ARRAY(0x12345). The trouble is that the contents of the object were being mysteriously modified. It would be incorrect to think that a different object was being constructed or returned by some other unrelated code. I verified that I had the same object inside the subtract() call and outside. I verified the data structure prior to returning it and afterward. The reference to $_, not ARRAY(0x12345), was translated into a reference to ARRAY(0x12345)
To summarize, when writing overload functions that may be used for assignment, be sure to not write anything that includes $_ by reference. Always copy that out first.