memorybeef

Avoid memory leak from creating temporary objects


In Beef, I can have this code:

using System;

namespace Program
{
    class Foobar 
    {
        public int value;

        public this(int val)
        {
            value = val;
        }

        public static Foobar operator+(Foobar lhs, Foobar rhs)
        {
            let result = new Foobar();
            result.value = lhs.value + rhs.value;
            return result;
        }
    }

    class Program
    {
        public static void Main()
        {
            Foobar a = scope:: Foobar(5);
            Foobar b = scope:: Foobar(5);

            Foobar c = a + b;
            defer delete c;

            Console.Write("{}", c.value);

            Console.In.Read();

        }
    }
}

and that works fine, because I can easily delete the heap allocation made by the a+b operation. However, if I have:

using System;

namespace Program
{
    class Foobar 
    {
        public int value;

        public this(int val)
        {
            value = val;
        }

        public static Foobar operator+(Foobar lhs, Foobar rhs)
        {
            let result = new Foobar();
            result.value = lhs.value + rhs.value;
            return result;
        }
    }

    class Program
    {
        public static void Main()
        {
            Foobar a = scope:: Foobar(5);
            Foobar b = scope:: Foobar(5);
            Foobar x = scope:: Foobar(20);

            // Foobar c = (a + b) + x; // would leak

            Foobar temp = a + b;
            defer delete temp;

            Foobar c = temp + x;
            defer delete c;

            Console.Write("{}", c.value);
            Console.In.Read();

        }
    }
}

I have to do one addition at a time and delete each result one at a time.

Is there a better way to handle the destruction of these temporary variables?


Solution

  • The first thing I thought of was using a mixin which seems like the perfect solution however it doesn't support operators.

    One other alternative is to wrap the result in a struct then implicitly cast back the struct to FooBar. The idea is that we need an intermediate struct to handle everything on the stack.

    class Foobar {
        public int value;
    
        public this(int val) {
            value = val;
        }
    
        public static TempFoobar operator+(Foobar lhs, Foobar rhs) {
            let result = new Foobar(lhs.value + rhs.value);
            return TempFoobar(result);
        }
    }
    
    struct TempFoobar {
        Foobar result;
        public this(Foobar val) {
            result = val;
        }
    
        public void Replace(Foobar val) mut {
            delete result;
            result = val;
        }
    
        public static TempFoobar operator+(TempFoobar lhs, Foobar rhs) {
            var copy = lhs;
            let result = new Foobar(lhs.result.value + rhs.value);
            copy.Replace(result);
            return copy;
        }
    
        public static implicit operator Foobar(TempFoobar temp) {
             return temp.result;
        }
    }
    
    
    class Program {
        static mixin Add(Foobar lhs, Foobar rhs) {
            let result = scope:mixin Foobar(lhs.value + rhs.value);
            result
        }
    
        public static void Main() {
            Test();
            Console.In.Read();
        }
    
        public static void Test() {
            Foobar a = scope Foobar(5);
            Foobar b = scope Foobar(5);
            Foobar x = scope Foobar(20);
            //let c = Add!(Add!(a, b), x); //Mixin version
            Foobar c = (a + b) + x;
            defer delete c;
            Console.Write("{}", c.value);
        }
    }
    

    Granted, I would avoid operator overloading unless it really.. reallyyy makes sense, and would never return a heap instance from a function without a keyword such as 'create' or 'new' in the function name.

    Side note, in this specific case you can just use an int as an intermediate object

        public static int operator+(Foobar lhs, Foobar rhs)
        {
            return lhs.value + rhs.value;
        }
    
        public static int operator+(int lhs, Foobar rhs)
        {
            return lhs + rhs.value;
        }
    
        public static implicit operator Foobar(int temp) {
             return new Foobar(temp);
        }
    

    Again, I don't recommend doing this.