Thread overview
opDispatch and @property setters
Jun 21, 2016
Lodovico Giaretta
Jun 21, 2016
ag0aep6g
Jun 21, 2016
Lodovico Giaretta
June 21, 2016
Hi,

I'm trying to achieve perfect forwarding of any invocation from the wrapper to the wrapped item, but I'm having a bad time with @property.

Suppose this is my wrapper (with all logic stripped):

    struct Wrapper(T)
    {
        private T wrapped;

        template hasAssignableProperty(string name, Arg)
        {
            enum bool hasAssignableProperty = is(typeof(
            (ref T val, ref Arg arg)
            {
                mixin("val." ~ name ~ " = arg;");
            }));
        }

        @property void opDispatch(string name, Arg)(Arg arg)
            if (hasAssignableProperty!(name, Arg))
        {
            pragma(msg, "@property ", name, " ", Arg);
            mixin("return wrapped." ~ name ~ " = arg;");
        }
        auto opDispatch(string name, Args...)(Args args)
        {
            static if (Args.length > 0)
            {
                pragma(msg, name, " ", Args);
                mixin("return wrapped." ~ name ~ "(args);");
            }
            else
            {
                pragma(msg, name);
                mixin("return wrapped." ~ name ~ ";");
            }
        }
    }

It correctly handles any property, but the existence of the @property setter dispatcher makes me unable to use any method that accepts just one parameter, because the compiler tries to resolve it with the first opDispatch overload, misinterpreting it as a setter call.

Here is an example:

    struct Foo
    {
        private int _x;
        @property int x() { return _x; }
        @property void x(int newx) { _x = newx; }

        long foo(string a, double b) const { return 0; }
        void bar() {}
        int baz(int val) { return val; } // <-- method with exactly one argument
    }

    void main()
    {
        alias WrappedFoo = Wrapper!Foo;
        WrappedFoo wf;

        wf.x = 3;
        assert(wf.x == 3);

        long foores = wf.foo("hello", 3.14);
        wf.bar;

        int bazres = wf.baz(42); // ERROR: no property 'baz' (it's trying to use the first opDispatch overload, while the second would be ok)
    }

Any way around this issue?

Thank you in advance, and sorry if the question is silly and I'm just missing something stupid.

Lodovico Giaretta
June 21, 2016
On 06/21/2016 10:48 PM, Lodovico Giaretta wrote:
>      struct Wrapper(T)
>      {
>          private T wrapped;
>
>          template hasAssignableProperty(string name, Arg)
>          {
>              enum bool hasAssignableProperty = is(typeof(
>              (ref T val, ref Arg arg)
>              {
>                  mixin("val." ~ name ~ " = arg;");
>              }));
>          }
>
>          @property void opDispatch(string name, Arg)(Arg arg)
>              if (hasAssignableProperty!(name, Arg))
>          {
>              pragma(msg, "@property ", name, " ", Arg);
>              mixin("return wrapped." ~ name ~ " = arg;");
>          }
>          auto opDispatch(string name, Args...)(Args args)
>          {
[...]
>          }
>      }
[...]
>      struct Foo
>      {
[...]
>          int baz(int val) { return val; } // <-- method with exactly one
> argument
>      }
>
>      void main()
>      {
[...]
>          int bazres = wf.baz(42); // ERROR: no property 'baz' (it's
> trying to use the first opDispatch overload, while the second would be ok)
>      }
>
> Any way around this issue?

Works when you change the return type of the the @property opDispatch to auto, so that it can return the result. It's a little weird, but D does support calling functions with assignment syntax.

Alternatively, maybe you can actually check for the @property attribute in hasAssignableProperty. See FunctionAttribute/functionAttributes in std.traits [1]. I haven't tested this.


[1] http://dlang.org/phobos/std_traits.html#.FunctionAttribute
June 21, 2016
On Tuesday, 21 June 2016 at 21:11:39 UTC, ag0aep6g wrote:
> Works when you change the return type of the the @property opDispatch to auto, so that it can return the result. It's a little weird, but D does support calling functions with assignment syntax.
>
> Alternatively, maybe you can actually check for the @property attribute in hasAssignableProperty. See FunctionAttribute/functionAttributes in std.traits [1]. I haven't tested this.
>
>
> [1] http://dlang.org/phobos/std_traits.html#.FunctionAttribute

Thank you very much!

I managed to get it to work by using this code:

    @property auto opDispatch(string name, Arg)(Arg arg)
    {
        mixin("return _p_data." ~ name ~ " = arg;");
    }

> It's a little weird, but D does support calling functions with assignment syntax.

Definitely strange.