Jump to page: 1 2
Thread overview
Disabling opAssign in a type disabled all the opAssigns of an aliased type?
Jul 30, 2018
aliak
Jul 30, 2018
Alex
Jul 30, 2018
aliak
Jul 30, 2018
Alex
Jul 30, 2018
aliak
Jul 30, 2018
aliak
Jul 30, 2018
Simen Kjærås
Jul 30, 2018
aliak
Jul 31, 2018
Simen Kjærås
Jul 31, 2018
Simen Kjærås
Aug 02, 2018
aliak
Aug 01, 2018
Simen Kjærås
July 30, 2018
Is this a bug?

If not is there a workaround?

I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type?


struct A {
    void opAssign(int) {}
}
struct B {
    A a;
    alias a this;
    @disable void opAssign(float);
}

void main() {
    B b;
    b = 3;
}

Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable

Cheers,
- Ali
July 30, 2018
On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote:
> Is this a bug?
>
> If not is there a workaround?
>
> I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type?
>
>
> struct A {
>     void opAssign(int) {}
> }
> struct B {
>     A a;
>     alias a this;
>     @disable void opAssign(float);
> }
>
> void main() {
>     B b;
>     b = 3;
> }
>
> Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable
>
> Cheers,
> - Ali

What happens if you omit the @disable line?
July 30, 2018
On Monday, 30 July 2018 at 18:47:06 UTC, Alex wrote:
> On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote:
>> Is this a bug?
>>
>> If not is there a workaround?
>>
>> I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type?
>>
>>
>> struct A {
>>     void opAssign(int) {}
>> }
>> struct B {
>>     A a;
>>     alias a this;
>>     @disable void opAssign(float);
>> }
>>
>> void main() {
>>     B b;
>>     b = 3;
>> }
>>
>> Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable
>>
>> Cheers,
>> - Ali
>
> What happens if you omit the @disable line?

Compiles ok then.
July 30, 2018
On Monday, 30 July 2018 at 19:33:45 UTC, aliak wrote:
> On Monday, 30 July 2018 at 18:47:06 UTC, Alex wrote:
>> On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote:
>>> [...]
>>
>> What happens if you omit the @disable line?
>
> Compiles ok then.

So... is this a valid workaround? ;)
July 30, 2018
On Monday, 30 July 2018 at 20:20:15 UTC, Alex wrote:
> On Monday, 30 July 2018 at 19:33:45 UTC, aliak wrote:
>> On Monday, 30 July 2018 at 18:47:06 UTC, Alex wrote:
>>> On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote:
>>>> [...]
>>>
>>> What happens if you omit the @disable line?
>>
>> Compiles ok then.
>
> So... is this a valid workaround? ;)

Hehe. Unfortunately not. It's for a proxy type that I need to disallow assignment to. But the proxy type uses alias to a T. So if T has a custom opAssign then bye bye functionality.
July 30, 2018
On Monday, 30 July 2018 at 20:38:33 UTC, aliak wrote:
> On Monday, 30 July 2018 at 20:20:15 UTC, Alex wrote:
>> On Monday, 30 July 2018 at 19:33:45 UTC, aliak wrote:
>>> On Monday, 30 July 2018 at 18:47:06 UTC, Alex wrote:
>>>> On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote:
>>>>> [...]
>>>>
>>>> What happens if you omit the @disable line?
>>>
>>> Compiles ok then.
>>
>> So... is this a valid workaround? ;)
>
> Hehe. Unfortunately not. It's for a proxy type that I need to disallow assignment to. But the proxy type uses alias to a T. So if T has a custom opAssign then bye bye functionality.

The actual code is here if you're curious:

https://github.com/aliak00/optional/pull/16/commits/93d51d790d313be3b108df2bd8b3699adc898bd0

Right now I've only:

@disable this(); // Do not allow user creation of a Dispatcher
@disable this(this) {} // Do not allow blitting either

But I also want to

@disable void opAssign(U)(Dispatcher!U)

So that you can't reassign to the Dispatcher (i.e. proxy type)

But if I do that then the opAssigns in the Optional!T cease functioning.

July 30, 2018
On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote:
> Is this a bug?
>
> If not is there a workaround?
>
> I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type?
>
>
> struct A {
>     void opAssign(int) {}
> }
> struct B {
>     A a;
>     alias a this;
>     @disable void opAssign(float);
> }
>
> void main() {
>     B b;
>     b = 3;
> }
>
> Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable


The workaround is to not disable opAssign. :p

Since this does work for other member functions that opAssign, I'm gonna say it's a bug - please file it in Bugzilla.

A perhaps better workaround than the above is to wrap A's opAssigns. Sadly, this can't be done with template mixins, since they don't overload with non-mixins. It can be done with string mixins, however. It's also possible to encapsulate all this in a nice little template:

struct A {
    void opAssign(int) {}
    void opAssign(float) {}
}
struct B {
    A a;
    alias a this;
    @disable void opAssign(float);
    mixin(wrap!(B, "opAssign"));
}

string wrap(T, string methodName)() {
    enum targetName = __traits(getAliasThis, T)[0];
    return `import std.traits : Parameters, ReturnType;
    static foreach (e; __traits(getOverloads, typeof(`~targetName~`), "`~methodName~`"))
        static if (!is(typeof({static assert(__traits(isDisabled, getOverload!(typeof(this), "`~methodName~`", Parameters!e)));})))
            ReturnType!e `~methodName~`(Parameters!e args) {
                return __traits(getMember, `~targetName~`, "`~methodName~`")(args);
            }`;
}

template getOverload(T, string name, Args...) {
    import std.traits : Parameters;
    import std.meta : AliasSeq;
    template impl(overloads...) {
        static if (overloads.length == 0) {
            alias impl = AliasSeq!();
        } else static if (is(Parameters!(overloads[0]) == Args)) {
            alias impl = overloads[0];
        } else {
            alias impl = impl!(overloads[1..$]);
        }
    }
    alias getOverload = impl!(__traits(getOverloads, T, name));
}

unittest {
    B b;
    b = 3;
    static assert(!__traits(compiles, b = 3f));
}

And that's enough magic for me for one night.

--
  Simen

July 30, 2018
On Monday, 30 July 2018 at 20:54:28 UTC, Simen Kjærås wrote:
> On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote:
>> Is this a bug?
>>
>> If not is there a workaround?
>>
>> I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type?
>>
>>
>> struct A {
>>     void opAssign(int) {}
>> }
>> struct B {
>>     A a;
>>     alias a this;
>>     @disable void opAssign(float);
>> }
>>
>> void main() {
>>     B b;
>>     b = 3;
>> }
>>
>> Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable
>
>
> The workaround is to not disable opAssign. :p
>
> Since this does work for other member functions that opAssign, I'm gonna say it's a bug - please file it in Bugzilla.
>
> A perhaps better workaround than the above is to wrap A's opAssigns. Sadly, this can't be done with template mixins, since they don't overload with non-mixins. It can be done with string mixins, however. It's also possible to encapsulate all this in a nice little template:
>
> struct A {
>     void opAssign(int) {}
>     void opAssign(float) {}
> }
> struct B {
>     A a;
>     alias a this;
>     @disable void opAssign(float);
>     mixin(wrap!(B, "opAssign"));
> }
>
> string wrap(T, string methodName)() {
>     enum targetName = __traits(getAliasThis, T)[0];
>     return `import std.traits : Parameters, ReturnType;
>     static foreach (e; __traits(getOverloads, typeof(`~targetName~`), "`~methodName~`"))
>         static if (!is(typeof({static assert(__traits(isDisabled, getOverload!(typeof(this), "`~methodName~`", Parameters!e)));})))
>             ReturnType!e `~methodName~`(Parameters!e args) {
>                 return __traits(getMember, `~targetName~`, "`~methodName~`")(args);
>             }`;
> }
>
> template getOverload(T, string name, Args...) {
>     import std.traits : Parameters;
>     import std.meta : AliasSeq;
>     template impl(overloads...) {
>         static if (overloads.length == 0) {
>             alias impl = AliasSeq!();
>         } else static if (is(Parameters!(overloads[0]) == Args)) {
>             alias impl = overloads[0];
>         } else {
>             alias impl = impl!(overloads[1..$]);
>         }
>     }
>     alias getOverload = impl!(__traits(getOverloads, T, name));
> }
>
> unittest {
>     B b;
>     b = 3;
>     static assert(!__traits(compiles, b = 3f));
> }
>
> And that's enough magic for me for one night.
>
> --
>   Simen

Heheh .... Amazing! In today's episode of extreme D (why is that not a thing?), we give you a "nice little template" :p

https://issues.dlang.org/show_bug.cgi?id=19130

Would it take much to fix it up to use with templated opAssigns as well?

I tried for a bit and got stuck with trying to get parameters and now I'm giving up for the time being.

struct A {
    void opAssign(int) {}
    void opAssign()(float) {}
}
struct B(T) {
    A a;
    alias a this;
    @disable void opAssign(U)(B!U);

    import std.traits : Parameters, ReturnType;
    static foreach (t; __traits(getOverloads, A, "opAssign", true)) {
        static if (is(typeof(t.stringof))) {
            pragma(msg, t.stringof, " - ", Parameters!t);
        } else {
            pragma(msg, typeof(t), " - ", Parameters!t);
        }
    }
}

The Parameters!t of the template overloads all come out as "int" but only if there's the non-template opAssign(int) in A. If you remove that then you get errors. So something is fishy.

Also I realized that it's just 2 opAssigns in the aliased Optional!T type for my specific use case so maybe, err... copy pasta them in.

Cheers,
- Ali


July 31, 2018
On Monday, 30 July 2018 at 23:41:09 UTC, aliak wrote:
> https://issues.dlang.org/show_bug.cgi?id=19130

Beautiful. :)

> Would it take much to fix it up to use with templated opAssigns as well?

I spent half an hour doing silly things, then I came up with this:

struct A {
    void opAssign(int) {}
    void opAssign(float) {}
    void opAssign(T)(T t) if (is(T == string)) {}
}
struct B {
    A a;
    alias a this;
    @disable void opAssign(float);
    mixin(wrap!(B, "opAssign"));
    auto opAssign(T...)(T args)
    if (__traits(compiles, a.opAssign(args)))
    {
        // Look ma, no magic!
        return a.opAssign(args);
    }
}
unittest {
    B b;
    b = "Foo!";
}

(Remaining code as in my last post)

Yeah, it really is that simple, since specific overloads are tried before templates.

--
  Simen
July 31, 2018
On Tuesday, 31 July 2018 at 07:01:33 UTC, Simen Kjærås wrote:
>     auto opAssign(T...)(T args)

Admittedly, one rarely uses multi-argument opAssign, but for those rare occasions... :p

--
  Simen
« First   ‹ Prev
1 2