www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Trying to forward unwrapped opDispatch names to alias this

reply aliak <something something.com> writes:
I have a scenario where I'm wrapping functionality for a type, 
but only if the contained type has a member. I want those to take 
precedence. If the member is not there, then I want to delegate 
to an aliases type via alias this.

I get an error here when I call b.p. Even though property p is in 
type A and there's an alias a this in B.

Anyway to do this?

struct A {
      property string p() {
         return "A";
     }
      property void p(string str) {}
}

struct B(T) {
     T t;
     A a;
     alias a this;
     auto opDispatch(string name)() if (hasMember!(T, name)) {
         return mixin("t." ~ name);
     }
}

void main() {
     B!int b;
     b.max.writeln;
     b.p.writeln; // Error: no property 'p' for type 'B!int'
}

Cheers,
- Ali
Feb 18 2018
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 19 February 2018 at 00:42:05 UTC, aliak wrote:
 struct B(T) {
     T t;
     A a;
     alias a this;
     auto opDispatch(string name)() if (hasMember!(T, name)) {
         return mixin("t." ~ name);
Did you perhaps mean `A` instead of `T` here? cuz in your code T is int, not the struct.
Feb 18 2018
parent reply aliak <something something.com> writes:
On Monday, 19 February 2018 at 01:00:23 UTC, Adam D. Ruppe wrote:
 On Monday, 19 February 2018 at 00:42:05 UTC, aliak wrote:
 struct B(T) {
     T t;
     A a;
     alias a this;
     auto opDispatch(string name)() if (hasMember!(T, name)) {
         return mixin("t." ~ name);
Did you perhaps mean `A` instead of `T` here? cuz in your code T is int, not the struct.
I don't think I did :p T is the wrapped type. So if T has a member (in the example it's the built in field "max") then forward that. If member not there then I figured the alias this would be used. I.e. in the example b.p should call A.p. I assume this should work because rules for alias this (as I understand) are to basically try if there's a member name that resolves the call, else forward to alias this.
Feb 19 2018
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Monday, 19 February 2018 at 08:28:22 UTC, aliak wrote:
 On Monday, 19 February 2018 at 01:00:23 UTC, Adam D. Ruppe 
 wrote:
 On Monday, 19 February 2018 at 00:42:05 UTC, aliak wrote:
 struct B(T) {
     T t;
     A a;
     alias a this;
     auto opDispatch(string name)() if (hasMember!(T, name)) {
         return mixin("t." ~ name);
Did you perhaps mean `A` instead of `T` here? cuz in your code T is int, not the struct.
I don't think I did :p T is the wrapped type. So if T has a member (in the example it's the built in field "max") then forward that. If member not there then I figured the alias this would be used. I.e. in the example b.p should call A.p. I assume this should work because rules for alias this (as I understand) are to basically try if there's a member name that resolves the call, else forward to alias this.
There is a related ticket, https://issues.dlang.org/show_bug.cgi?id=6434 However, not exactly facing this question. It seems, that currently opDispatch replaces alias this... (?) Nevertheless, not only T is the wrapped type, but also A with the alias. What to do, if both members have a property with the same name? And why?
Feb 20 2018
parent aliak <something something.com> writes:
On Tuesday, 20 February 2018 at 11:27:23 UTC, Alex wrote:
 There is a related ticket,
 https://issues.dlang.org/show_bug.cgi?id=6434
 However, not exactly facing this question.
Should that ticket be marked as resolved? The issue is for alias this to be considered before opDispatch but there were no arguments after giving reasons for that being a bad idea. And opDispatch is considered before (and disables alias this) now it seems?
 It seems, that currently opDispatch replaces alias this... (?)
 Nevertheless, not only T is the wrapped type, but also A with 
 the alias.
 What to do, if both members have a property with the same name? 
 And why?
Good question, should it be dealt with in the same way as this is dealt with? struct S { void f() {writeln("S");} } struct A { S s; alias s this; void f() {writeln("A");} } A().f; // prints "A" However, the above behavior seems dangerous maybe and could lead to silent bugs. I'm thinking if A.f was not defined, then A().f would print "S". But then someone comes along and defines an A.f and the user of A now gets different functionality without even knowing about it. Hrm.. one is not sure how one feels :p But how about if they do not have the same name? Currently AliasThis says: "If the member is a class or struct, undefined lookups will be forwarded to the AliasThis member." So opDispatch, even though it's constrained, is not considered undefined if it doesn't pass. In those cases should it not be forwarded to the alias this? If not, then why does a constrained function not qualify as being undefined if the constraint doesn't pass? Consider: struct A { void defined() {} } struct B { void opDispatch(string name)() if (name == "defined") {} } void main() { A().defined; // ok B().defined; // ok A().undefined; B().undefined; } Errors are: * Error: no property 'undefined' for type 'A', did you mean 'defined'? * Error: no property 'undefined' for type 'B' If you then define a struct S that contains a function named "undefined" and add a "S s; alias s this;" inside A and B, you get: * Error: no property 'undefined' for type 'B' But not for A. Shouldn't the behavior here be consistent? Or why not? Churr, - Ali
Feb 20 2018
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 19 February 2018 at 08:28:22 UTC, aliak wrote:
 T is the wrapped type. So if T has a member (in the example 
 it's the built in field "max") then forward that.
Oh, I see what you mean. So the problem is that built in types don't have "members" per se, they have "magic". The built in properties don't count to `hasMember`. You could probably do `if(__traits(compiles, mixin("a." ~ member))` though.
 I assume this should work because rules for alias this (as I 
 understand) are to basically try if there's a member name that 
 resolves the call, else forward to alias this.
yeah, just built in properties/members don't pass the same existence checks so that's confusing your template constraint.
Feb 20 2018
parent aliak <something something.com> writes:
On Tuesday, 20 February 2018 at 16:12:17 UTC, Adam D. Ruppe wrote:
 On Monday, 19 February 2018 at 08:28:22 UTC, aliak wrote:
 T is the wrapped type. So if T has a member (in the example 
 it's the built in field "max") then forward that.
Oh, I see what you mean. So the problem is that built in types don't have "members" per se, they have "magic". The built in properties don't count to `hasMember`. You could probably do `if(__traits(compiles, mixin("a." ~ member))` though.
 I assume this should work because rules for alias this (as I 
 understand) are to basically try if there's a member name that 
 resolves the call, else forward to alias this.
yeah, just built in properties/members don't pass the same existence checks so that's confusing your template constraint.
I believe the same happens if it's not a built in property. You can replace B!int with B!S and have S contains a function "f" (for instance), then whether or the not constraint on opDispatch is: __traits(compiles, mixin("t." ~ name)) or hasMember(T, name) You still get: b.p.writeln; // Error: no property 'p' for type 'B!(S)'
Feb 20 2018