www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Disabling opAssign in a type disabled all the opAssigns of an aliased

reply aliak <something something.com> writes:
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
Jul 30 2018
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
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?
Jul 30 2018
parent reply aliak <something something.com> writes:
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.
Jul 30 2018
parent reply Alex <sascha.orlov gmail.com> writes:
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? ;)
Jul 30 2018
parent reply aliak <something something.com> writes:
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.
Jul 30 2018
parent aliak <something something.com> writes:
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.
Jul 30 2018
prev sibling next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Jul 30 2018
parent reply aliak <something something.com> writes:
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
Jul 30 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Jul 31 2018
next sibling parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Jul 31 2018
prev sibling parent aliak <something something.com> writes:
On Tuesday, 31 July 2018 at 07:01:33 UTC, Simen Kjærås wrote:
 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
Oh nice! So you don't even need all that mixin magic and can get away with: struct A { void opAssign(int) {} disable void opAssign(float) {} void opAssign(T)(T t) if (is(T == string)) {} } struct B(T) { A a; alias a this; disable void opAssign(B!T); mixin(wrap!(B, "opAssign")); } string wrap(T, string methodName)() { enum targetName = __traits(getAliasThis, T)[0]; return `auto `~methodName~`(T...)(T args) if (__traits(compiles, `~targetName~`.`~methodName~`(args))) { return `~targetName~`.`~methodName~`(args); }`; } void main() { B!int b; b = 3; b = "hello"; static assert(!__traits(compiles, { b = 3f; } )); static assert(!__traits(compiles, { b = b; } )); }
Aug 02 2018
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/30/18 2:30 PM, 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
 
OK, so one thing to learn in D, you can't hijack stuff. When you override a function, you have to override ALL the overloads. What you have done is defined opAssign as ONLY accepting a float, and then disabling any calls to it. This is obfuscated somewhat by the fact that 3 can be a float as well. So even if you didn't disable the opAssign(float), it would still call that function. You can get around it by defining a forwarding function for the overloads you want to let through: disable void opAssign(float); auto opAssign(int x) { return a.opAssign(x); } -Steve
Jul 31 2018
parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 31 July 2018 at 20:40:25 UTC, Steven Schveighoffer 
wrote:
 OK, so one thing to learn in D, you can't hijack stuff. When 
 you override a function, you have to override ALL the overloads.
I could have sworn I tested this before I wrote that it's a bug: struct A { void fun(int) {} } struct B { A a; alias a this; disable void fun(float); } void main() { B b; b.fun(3); } I was surprised to see it work, as I also thought it'd be a hijacking issue. Turns out, it doesn't actually work. I must have made a typo when I tried it. Ah well. -- Simen
Jul 31 2018