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

Hi,
First up the code:
use warnings; package Grief; use overload '=' => \&_copy; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; typedef struct { long num; } Number; SV * new(SV * x) { Number * num_obj; SV * obj_ref, * obj; New(1, num_obj, 1, Number); if(num_obj == NULL) croak("Failed to allocate memory in new() function"); obj_ref = newSViv(0); obj = newSVrv(obj_ref, "Grief"); num_obj->num = SvUV(x); sv_setiv(obj, (IV)num_obj); SvREADONLY_on(obj); return obj_ref; } SV * _get_val(SV * obj) { return newSVuv(((Number*)SvIV(SvRV(obj)))->num); } void _set_val(SV * obj, SV * x) { ((Number*)SvIV(SvRV(obj)))->num = SvUV(x); } SV * _copy(SV * arg_obj, SV * second, SV * third) { Number * num_obj; SV * obj_ref, * obj; New(1, num_obj, 1, Number); if(num_obj == NULL) croak("Failed to allocate memory in _copy() function"); obj_ref = newSViv(0); obj = newSVrv(obj_ref, "Grief"); num_obj->num = ((Number*)SvIV(SvRV(arg_obj)))->num; sv_setiv(obj, (IV)num_obj); SvREADONLY_on(obj); return obj_ref; } SV * _set_from_existing(SV * arg_obj) { Number * num_obj; SV * obj_ref, * obj; New(1, num_obj, 1, Number); if(num_obj == NULL) croak("Failed to allocate memory in _set_from_existing() functio +n"); obj_ref = newSViv(0); obj = newSVrv(obj_ref, "Grief"); num_obj->num = ((Number*)SvIV(SvRV(arg_obj)))->num; sv_setiv(obj, (IV)num_obj); SvREADONLY_on(obj); return obj_ref; } void DESTROY(SV * obj) { printf("Destroying ..."); Number * number = (Number*)SvIV(SvRV(obj)); Safefree(number); printf("... destroyed\n"); } EOC $num1 = Grief::new(113); $num2 = $num1; # Assign using _copy() $num3 = _set_from_existing($num1); # Assign using _set_from_existing() _set_val($num1, 555); print _get_val($num2),"\n", _get_val($num3), "\n"; __END__ Outputs: 555 113 Destroying ...... destroyed Destroying ...... destroyed
The questions:
1) Can someone explain to me why $num2 and $num3 have different values (555 and 113 respectively) ?
2) How do I rewrite the _copy() function (which is called via the overloading of the assignment operator) in such a way that $num2 will be a copy of the initial value of $num1 (as is $num3), rather than some sort of alias/pointer/reference to $num1 ?

What's got me really confused is that _copy() and _set_from_existing() are identical functions ... and yet they return different things (as evidenced by the fact that $num2 and $num3 are quite different beasts).

The keen observer will notice that, although there are 3 variables, only 2 of them get destroyed - it's $num2 that does not have to be cleaned up.

Cheers,
Rob

Replies are listed 'Best First'.
Re: Overloading '=' doesn't DWIM
by jettero (Monsignor) on May 01, 2007 at 15:57 UTC

    You can't overload '=' actually. They give you a half-way solution, but it's deemed un-perl-ish to allow the overload of '='. It would "go against the camel-hair" to actually overload the assignment operator.

    I have found that, not only is it un-intuitive, but it just plain never works how I expect. I think your LHS would have to be an object also, but it could be something else going wrong. The simplest solution is to just call your copy function directly instead of trying to overload '='. Even if you get it to work, it's going to cause trouble again some day.

    I overload most of the other operators from time to time, but I never bother trying to get the shallow-copy to work.

    -Paul

      They give you a half-way solution

      I wonder why do they do that ? If I can use overload '=' => \&copy; then that ought to do something useful. (Ok ... so perhaps, in the right hands, it does do something useful.) And why is it considered un-perlish to overload '=' ? (Well ... I probably wouldn't understand the answer to that question, anyway.)

      call your copy function directly instead of trying to overload '='

      Yes - that's pretty much what the demo I posted does. Except that it contains an unnecessary function. The 'set_from_existing' function can be removed from the Inline::C section, the perl code in the script changed to:
      $num1 = Grief::new(113); $num2 = $num1; # Assign using _copy() $num3 = _copy($num1, '', ''); # Assign using _copy() _set_val($num1, 555); print _get_val($num2),"\n", _get_val($num3), "\n";
      and the rest of the post remains essentially unchanged. It still baffles me that $num3 gets created as a separate object, but $num2 gets created (by exactly the same function) as some sort of semi-object that (to begin with) is tied in some way to $num1, and never gets DESTROYed (even if the tie is subsequently broken).

      Thanks Paul. I can see no reason to disagree with your advice.

      Cheers,
      Rob

        I'm not sure if it is relevant--you may be aware already--but I added a couple of printfs to your code:

        SV * _copy( SV * arg_obj, SV * second, SV * third ) { Number * num_obj; SV * obj_ref, * obj; printf( "_copy:%p %p %p\n", arg_obj, second, third ); ... SV * _set_from_existing( SV * arg_obj ) { Number * num_obj; SV * obj_ref, * obj; printf( "_set::%p\n", arg_obj ); ...

        And it appears that the overloading is not causing your _copy() function to be called:

        Finished Build Compile Stage _set::0185E260 555 113 Destroying ...... destroyed Destroying ...... destroyed

        Which (as best I can tell) explains the output completely:

        $$num1 = Grief::new( 113 ); $num2 = $num1; # Assign using _copy( ) $num3 = _set_from_existing( $num1 ); # Assign using _set_from_existing +( ) _set_val( $num1, 555 ); print _get_val( $num2 ),"\n", _get_val( $num3 ), "\n"; __END__

        $num2, in the absence of the overloading is simply an alias to $num1, whereas $num3 is explicitely as new object with the same value. When you set $num1 to 555, you are also setting $num2, as they are both references to the same object.

        All of which is probably not news to you. The only real question as far as I can see is if overload can't or won't allow you to overload assignment, why doesn't use overload '=' => \&_copy; throw an error?


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.