July 06, 2018
On Friday, 6 July 2018 at 15:57:27 UTC, Timoses wrote:
> On Friday, 6 July 2018 at 15:33:18 UTC, Michael wrote:
>>
>> This is definitely to do with my use of the setter syntax, which maybe I am misunderstanding? Because if I change it to a normal function call like so:
>>
>> a.beliefs(Operator.create());
>>
>> then it complains if I use ref, and doesn't complain if I don't.
>
> You can try removing the "auto" from the Operator class method
>
> // Error: returning dict escapes a reference to local variable dict
> false
>         static ref create()
>         {
>             double[int] dict;
>             dict[2] = 1.0;
>             dict[1] = 0.0;
>             return dict;
>         }
>
> That indicates that 'dict' is actually returned by value (at least the reference). This sounds a bit confusing.
> AFAIK, in above code a reference 'dict' is created pointing towards memory that stores that dictionary entries. The reference itself is a "pointer"(?) which will "die" when running out of the method's scope. This pointer lives on the function's stack. So returning this "pointer" (essentially a value type, e.g. int) will return it by value.
>
> You could return a reference like so:
>
>     class Operator
>     {
>         static double[int] hdict;
>         static this()
>         {
>              hdict[2] = 1.0;
>              hdict[1] = 0.0;
>         }
>         static ref create()
>         {
>             return hdict;
>         }
>     }
>
> then
>
>> a.beliefs(Operator.create());
>
> should work on the "ref" setter.

Oh of course, as it's a reference type, the "value" being returned by 'auto ref' is the value of the reference to the array anyway, so I am already passing the reference. Not sure how I managed to get really confused about that. Then, forcing the function to return a reference to a local reference to a heap alloc. array is obviously a problem. This makes way more sense now. Thank you again, Timoses, and I hope you enjoyed the result, Ali.

I guess I just need to be careful with the setter/getter approach, in case I accidentally invoke the getter to return a reference, which I then overwrite with another reference. I don't know if that's worthy of adding some kind of compiler warning, or if I'm just the stupid one to get caught by that, but I'm glad it's resolved.

At least it was, technically, working before by leaking access to the private member variable and overwriting it.
July 06, 2018
On Friday, 6 July 2018 at 15:51:34 UTC, Michael wrote:
> Also, yes, I am using the setter method to play around with the precision of the double values, and do some normalising.
While writing I realized that the following is even the case without the 'ref' parameter:
The caller of the setter will still be able to change the content of your private data after you checked it for validity. Take following example:

    class A
    {
        private static string[] _disallowed = ["damn"];
    	private string[int] _dict;

        // called before and after every member method call
        invariant
        {
            import std.algorithm : any, canFind;
            // don't allow _dict to contain any from _disallowed
             assert(!this._dict.byValue()
                    .any!((entry) => _disallowed.canFind(entry)));
        }

        @property dict(string[int] dict)
        {
             // checks ...
            this._dict = dict;
        }

        void check() {
            import std.stdio : writeln;
            writeln(this._dict);
        }
    }

    unittest
    {
        string[int] loc;
        auto a = new A();
        loc[1] = "hello john";
        a.dict = loc; // using the @property setter
        loc[1] = "damn";
        a.check;
    }

What might be safer is using 'opIndexAssign'

        void opIndexAssign(string value, int key)
        {
            import std.algorithm.searching : canFind;
            if (!_disallowed.canFind(value))
                this._dict[key] = value;
        }

and also duping the dictionary that is handed to the Agent

        @property dict(string[int] dict)
        {
             // checks ...
            this._dict = dict.dup;
        }

So you can call

        string[int] loc;
        auto a = new A();
        loc[1] = "hello john";
        a.dict = loc;
        a[1] = "damn";
        loc[1] = "damn";
        a.check;

without assigning bad values.



> I always want it to access the setter method, but I had hoped that, given it's an associative array, that the creation of the object in create() would simply create it on the heap and I could pass back a reference. It seems that I was incorrect, as if I set create() to explicitly return a reference, it complains about returning a reference to a local variable. Any advice on the best way to pass it as a reference?

I suppose this might already answer your question:
https://forum.dlang.org/post/edrejkakhaylivlqjaqe@forum.dlang.org

July 06, 2018
On Friday, 6 July 2018 at 16:24:03 UTC, Timoses wrote:
> On Friday, 6 July 2018 at 15:51:34 UTC, Michael wrote:
>> [...]
> While writing I realized that the following is even the case without the 'ref' parameter:
> The caller of the setter will still be able to change the content of your private data after you checked it for validity. Take following example:
>
> [...]

I understand your warning, but as the array is created purely to assign to the agent's beliefs, it only appears in the main() function as a rhs reference, which is passed to the agent, the reference is stored in the member variable, and then the main function continues, holding no reference to that array outside of agent's agent.beliefs getter function. Appreciate the heads up, though.
1 2
Next ›   Last »