Search Unity

Operator overloading

Discussion in 'Scripting' started by SparrowGS, Jan 18, 2019.

  1. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    I don't know if it's what i'm suppose to be looking for, but i'm assuming so because that's how you override Equal and ToString.

    Say we have this struct
    Code (CSharp):
    1. public struct SomeStruct {
    2.  
    3. public float valueA;
    4. public int valueB;
    5. public bool valueC;
    6.  
    7. }
    how can I say
    Code (CSharp):
    1. SomeStruct ss;
    2.  
    3. void Blah(SomeStruct ss){
    4.  
    5. this.ss += ss;
    6. }

    I never did this before but I thought you just need to write the logic(a+a, b+b, w/e with c) in some override, I tried searching google but I don't think I'm verbalizing my question all that good to it, haha.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,536
    SparrowGS likes this.
  3. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    Sweet, thanks. ;)

    Yeah, I know that, thanks for the heads up though.
     
  4. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    Just to clarify, if I have this for example:
    Code (CSharp):
    1. public struct SomeStruct {
    2.  
    3. float value;
    4.  
    5. public static SomeStruct operator *(SomeStruct a, SomeStruct b){
    6.  
    7. return new SomeStruct(a.value * b.value); //suppose there's a constructor, heh
    8. }
    9. }
    10.  
    and somewhere I say
    Code (CSharp):
    1. someStructVariable *= otherSomeStructVariable;
    Am I guaranteed that 'someStructVariable' will be what is 'a' in the operator declartion and 'otherSomeStructVariable' be 'b'?
    *I know that doesn't really matter in this example.
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,536
    So lets see what actually happens under the hood in IL.

    Here I wrote up your example:
    https://sharplab.io/#v2:C4LglgNgNAJ...1PUoJcbnFw6uvNzkAHYPQ0PMzjURiqq91zexwM64qKAA=

    And we get this IL for your code where you use *=:
    Code (csharp):
    1.  
    2.         IL_0019: ldloc.0
    3.         IL_001a: ldloc.1
    4.         IL_001b: call valuetype SomeStruct SomeStruct::op_Multiply(valuetype SomeStruct, valuetype SomeStruct)
    5.         IL_0020: stloc.0
    6.  
    What this code is saying is:
    Code (csharp):
    1.  
    2. load variable 0 onto stack (this would be 'a' in my code)
    3. load variable 1 onto stack (this would be 'b' in my code)
    4. call the static method SomeStruct.op_Multiply with signature that takes 2 SomeStruct's as parameters
    5. pop value at top of stack into variable 0 (this would be a in my code)
    6.  
    Note that how methods are called in IL is that you push N number of values onto the memory stack that match the method signature, then call the method. This is how values are actually passed in memory to methods... via the stack this way. Same with returns... when you return a value really the IL pushes the return value onto the stack, and the calling code pops it off the stack into its place in memory.

    Next, really the *= is just syntax sugar for a = a * b... and that's really just syntax sugar for a = SomeStruct.op_Multiply(a,b). Which compiles into that IL.

    Now when you say:
    I will say...

    The 'value' that is 'someStructVariable' will be what is in a. And the value that is 'otherSomeStructVariable' will be was in in b.

    But I'm not sure what assumptions you're making beyond that.

    It'd be like if you said:
    a = 3;
    b = 5;
    a *= b;

    In the op_Multiply of that, the 1st parameter will be 3, and the 2nd will be 5.

    But if you're hoping some sort of 'state' will pass along... well no. Because a struct by design shouldn't pass state (unless you use 'ref' or 'out').

    But order wise... yes... the order is guaranteed to be preserved. Otherwise non-commutative operations (division, subtraction, quaternion multiplication) wouldn't work. And since we know quat1 *= quat2 is guaranteed valid, we know it must respect order.
     
    SparrowGS likes this.
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,536
    As an aside and if you're at all interested in more about this topic... check out this spoiler.

    In your code I'd actually write the multiply method as:
    Code (csharp):
    1.  
    2.     public static SomeStruct operator *(SomeStruct a, SomeStruct b)
    3.     {
    4.         a.value *= b.value;
    5.         return a;
    6.     }
    7.  
    I say this because if you look at my link and how this version is implemented:
    https://sharplab.io/#v2:C4LglgNgNAJ...EM4G1PUoJcbnFw6uvNy1EYqqvfC7sZrc5AB2dSnYoA===

    Note that yours actually calls a new fuction (the constructor):
    Code (csharp):
    1.  
    2.         IL_0000: nop
    3.         IL_0001: ldarg.0
    4.         IL_0002: ldfld float32 SomeStruct::'value'
    5.         IL_0007: ldarg.1
    6.         IL_0008: ldfld float32 SomeStruct::'value'
    7.         IL_000d: mul
    8.         IL_000e: newobj instance void SomeStruct::.ctor(float32) //THIS LINE HERE
    9.         IL_0013: stloc.0
    10.         IL_0014: br.s IL_0016
    11.  
    Where as mine does not:
    Code (csharp):
    1.  
    2.         IL_0000: nop
    3.         IL_0001: ldarga.s a
    4.         IL_0003: ldflda float32 SomeStruct::'value'
    5.         IL_0008: dup
    6.         IL_0009: ldind.r4
    7.         IL_000a: ldarg.1
    8.         IL_000b: ldfld float32 SomeStruct::'value'
    9.         IL_0010: mul
    10.         IL_0011: stind.r4
    11.         IL_0012: ldarg.0
    12.         IL_0013: stloc.0
    13.         IL_0014: br.s IL_0016
    14.  
    (You could alternatively create a 3rd struct without calling your parameterized constructor)

    The problem here is you're actually loading a new stack frame to call that constructor. Which is actually less performant.
     
    SparrowGS likes this.
  7. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    I realize now writing the example with multiplication was a bad idea, I do need it for subtraction and division, I don't like messing with quaternions myself though.

    Thanks for break down and all the input man, you're really helpful. :D

    Thank you for that, I tend to do this kind of optimization from day 1(I wrote it like that in the example because it's more "conventional" / how it is on the microsoft docs) and seeing tutorials just going willy nilly with that sort of stuff made me think I'm obsessing over nothing, glad to have a reinforcement that I'm on the right path with this.(all in moderation ofc, I don't go crazy over it)