digitalmars.D - opDispatch and compile time parameters
- Nikolay (35/35) Oct 17 2015 I asked on SO question about opDispatch and compile time
- Meta (14/49) Oct 17 2015 There is another approach, which is to automatically generate a
- David Osborne (37/43) Oct 18 2015 I must have my answer not too long after you made this thread,
- Jack Applegame (25/25) Oct 19 2015 D template system is very powerful. This is more generic
- Nikolay (5/8) Oct 19 2015 Wow!
- Andrei Alexandrescu (15/23) Oct 19 2015 It is amazing indeed. We should make this a standard library artifact.
- Adam D. Ruppe (11/13) Oct 19 2015 Since CTFE started supporting it... this might actually be an
- Jonathan M Davis (12/25) Oct 19 2015 Yeah. It makes sense when you're dealing with an immutable
- Andrei Alexandrescu (2/27) Oct 19 2015 Urgh. That looks like quite a bit of a bummer. -- Andrei
- Andrei Alexandrescu (13/25) Oct 19 2015 This works too:
- Adam D. Ruppe (20/30) Oct 19 2015 It is still in the static data segment. Try this:
- Timon Gehr (12/42) Oct 19 2015 This is the worst part:
- Jonathan M Davis (5/16) Oct 19 2015 Oooo. Ouch. Yeah, that pushes it from being a good idea to
- Marc =?UTF-8?B?U2Now7x0eg==?= (6/23) Oct 20 2015 Yeah. But IMO it should still be allowed for immutable objects,
- Jonathan M Davis (23/47) Oct 20 2015 I believe that it's supposed to be possible to initialize an
- Andrei Alexandrescu (2/19) Oct 21 2015 Please file, thanks. -- Andrei
- Timon Gehr (9/29) Oct 21 2015 (I've filed it in 2013.
- Andrei Alexandrescu (6/37) Oct 21 2015 The quickest way to stop the bleeding is to disallow the code. It's
- Don (9/56) Oct 21 2015 Fundamentally the problem is that literals of mutable reference
- Timon Gehr (19/25) Oct 21 2015 I think considering "[x,y,z]" a 'literal' is a problem, but why is it
- Jonathan M Davis (12/16) Oct 21 2015 It's perfectly correct for immutable data if the member variable
- Nikolay (7/9) Oct 20 2015 Thanks for this notice. I edited question on SO and I removed
- Jack Applegame (2/12) Oct 19 2015 Done
- Steven Schveighoffer (17/60) Nov 13 2015 I just came across a need for this, thanks.
- Jakob Ovrum (9/24) Nov 13 2015 Usually you should explicitly instantiate opDispatch to see the
- Steven Schveighoffer (10/33) Nov 16 2015 It appears that you can't directly access the eponymous member, this
I asked on SO question about opDispatch and compile time parameters: http://stackoverflow.com/questions/32998781/opdispatch-and-compile-time-parameters Currently it looks like it is not possible to use opDispatch for non trivial template functions. I think opDispatch should get function name plus evaluated compile time arguments as first compile time parameter. E.g not just function name "b", but "b.p2!(int, 10)" See example from SO question: import std.stdio; class B{ auto p1(T)(T arg) { writeln( "p1: ", arg ); } auto p2(T, int C)(T s) { writeln( "p2: ", s, " / ", C); } } class C(T) { T b = new T; auto opDispatch(string s, Args...)(Args args) { mixin("b."~s)(args); } } void main() { auto b = new C!(B)(); //fine: compiler is smart enough b.p1("abc"); //oops: "no property 'p2' for type ..." b.p2!(int, 10)(5); auto origB = new B; //fine: origB.p2!(int, 10)(5); } Is it good idea for opDispatch improvement or may there is some other approach?
Oct 17 2015
On Saturday, 17 October 2015 at 15:31:00 UTC, Nikolay wrote:I asked on SO question about opDispatch and compile time parameters: http://stackoverflow.com/questions/32998781/opdispatch-and-compile-time-parameters Currently it looks like it is not possible to use opDispatch for non trivial template functions. I think opDispatch should get function name plus evaluated compile time arguments as first compile time parameter. E.g not just function name "b", but "b.p2!(int, 10)" See example from SO question: import std.stdio; class B{ auto p1(T)(T arg) { writeln( "p1: ", arg ); } auto p2(T, int C)(T s) { writeln( "p2: ", s, " / ", C); } } class C(T) { T b = new T; auto opDispatch(string s, Args...)(Args args) { mixin("b."~s)(args); } } void main() { auto b = new C!(B)(); //fine: compiler is smart enough b.p1("abc"); //oops: "no property 'p2' for type ..." b.p2!(int, 10)(5); auto origB = new B; //fine: origB.p2!(int, 10)(5); } Is it good idea for opDispatch improvement or may there is some other approach?There is another approach, which is to automatically generate a declaration that more or less matches the one in b. Then you can call b.p2 or whatever else on the outer class, and it will be forwarded to the inner class. The implementation is a lot hairier than you might think, but I've got it mostly working now. I've been meaning to post an answer to your StackOverflow question, but I wanted to be sure that the implementation was complete and as bug-free as possible. You can find the code here: https://github.com/MetaLang/phobos/commit/6c5fa291a957f4050910064d1fa44a86ff92e760 Basically, you just do `mixin(forwardToMember!(b, "p1", "p2"));` inside your wrapper class and it will automatically declare the correct methods/properties/aliases for you. Let me know if it works for your usecase and I'll post it to StackOverflow as well.
Oct 17 2015
On Saturday, 17 October 2015 at 15:31:00 UTC, Nikolay wrote:I asked on SO question about opDispatch and compile time parameters: http://stackoverflow.com/questions/32998781/opdispatch-and-compile-time-parameters [...] Is it good idea for opDispatch improvement or may there is some other approach?I must have my answer not too long after you made this thread, but there is another approach: (copied from SO) You need to use the eponymous template pattern, and have an opDispatch function with your compile-time parameters inside an outer opDispatch template that takes the regular opDispatch string parameter. You can also have multiple inner opDispatch functions (and fields) that follow regular overload rules. import std.stdio; struct Foo { public template opDispatch(string name) { public string opDispatch() { return name; } public T opDispatch(T)() { return T.init; } public string opDispatch(T)(string s) { return name ~ ' ' ~ T.stringof ~ ' ' ~ s; } } } void main() { Foo foo; writeln( foo.bar ); writeln( foo.baz!int ); writeln( foo.foo!Foo("foo") ); } Produces the output: bar 0 foo Foo foo DPaste link: http://dpaste.dzfl.pl/6e5cfca8b702 I haven't fully explored the limitations of this approach, but it does work for simple cases.
Oct 18 2015
D template system is very powerful. This is more generic solution: import std.stdio; class B { auto p1(T)(T arg) { writeln( "p1: ", arg ); } auto p2(T, int C)(T s) { writeln( "p2: ", s, " / ", C); } } class C(T) { T b = new T; template opDispatch(string s) { template opDispatch(TARGS...) { auto opDispatch(ARGS...)(ARGS args) { static if(TARGS.length) return mixin("b." ~ s ~ "!TARGS(args)"); else return mixin("b." ~ s ~ "(args)"); } } } } void main() { auto b = new C!(B)(); b.p1("abc"); b.p2!(int, 10)(5); } http://dpaste.dzfl.pl/791c65d0e4ee
Oct 19 2015
On Monday, 19 October 2015 at 08:41:46 UTC, Jack Applegame wrote:D template system is very powerful. This is more generic solution: http://dpaste.dzfl.pl/791c65d0e4eeWow! I can't believe that it is possible and there is so straightforward way. You should post this answer to SO Thanks!
Oct 19 2015
On 10/19/2015 01:50 PM, Nikolay wrote:On Monday, 19 October 2015 at 08:41:46 UTC, Jack Applegame wrote:It is amazing indeed. We should make this a standard library artifact. Tangentially related: since when we allow field initialization with new? I was surprised to see that this works: struct A { int[] x = new int[10]; } void main() { import std.stdio; A a; writeln(a.x); } After all that talk about default constructors that don't do anything, it's ironic I didn't know about this language change :o). AndreiD template system is very powerful. This is more generic solution: http://dpaste.dzfl.pl/791c65d0e4eeWow! I can't believe that it is possible and there is so straightforward way. You should post this answer to SO Thanks!
Oct 19 2015
On Monday, 19 October 2015 at 18:16:15 UTC, Andrei Alexandrescu wrote:Tangentially related: since when we allow field initialization with new? I was surprised to see that this works:Since CTFE started supporting it... this might actually be an unintentional feature. Since the initializer is in a static context, the right hand side gets CTFE'd, which means that actually points to an array in the data segment... the *same* array in the data segment for all initializations (the pointer is just blitted over with the rest of init), which might be a bit surprising. I've seen a lot of people do this with classes not realizing it makes a static instance!
Oct 19 2015
On Monday, 19 October 2015 at 18:26:45 UTC, Adam D. Ruppe wrote:On Monday, 19 October 2015 at 18:16:15 UTC, Andrei Alexandrescu wrote:Yeah. It makes sense when you're dealing with an immutable object/arrays (and possibly const, assuming that it wasn't initialized with a mutable variable that was directly initialized via new), but it really makes no sense for mutable objects/arrays - not unless you have some weird case where you want each instance of your object to be able to share that member variable initially and then possibly stop sharing later. But given the high probability that someone is going to do this and shoot themselves in the foot, I think that we'd be a lot better off if we disallowed it. - Jonathan M DavisTangentially related: since when we allow field initialization with new? I was surprised to see that this works:Since CTFE started supporting it... this might actually be an unintentional feature. Since the initializer is in a static context, the right hand side gets CTFE'd, which means that actually points to an array in the data segment... the *same* array in the data segment for all initializations (the pointer is just blitted over with the rest of init), which might be a bit surprising. I've seen a lot of people do this with classes not realizing it makes a static instance!
Oct 19 2015
On 10/19/2015 02:44 PM, Jonathan M Davis wrote:On Monday, 19 October 2015 at 18:26:45 UTC, Adam D. Ruppe wrote:Urgh. That looks like quite a bit of a bummer. -- AndreiOn Monday, 19 October 2015 at 18:16:15 UTC, Andrei Alexandrescu wrote:Yeah. It makes sense when you're dealing with an immutable object/arrays (and possibly const, assuming that it wasn't initialized with a mutable variable that was directly initialized via new), but it really makes no sense for mutable objects/arrays - not unless you have some weird case where you want each instance of your object to be able to share that member variable initially and then possibly stop sharing later. But given the high probability that someone is going to do this and shoot themselves in the foot, I think that we'd be a lot better off if we disallowed it.Tangentially related: since when we allow field initialization with new? I was surprised to see that this works:Since CTFE started supporting it... this might actually be an unintentional feature. Since the initializer is in a static context, the right hand side gets CTFE'd, which means that actually points to an array in the data segment... the *same* array in the data segment for all initializations (the pointer is just blitted over with the rest of init), which might be a bit surprising. I've seen a lot of people do this with classes not realizing it makes a static instance!
Oct 19 2015
On 10/19/2015 02:26 PM, Adam D. Ruppe wrote:On Monday, 19 October 2015 at 18:16:15 UTC, Andrei Alexandrescu wrote:This works too: struct A { int[] x = new int[10]; } void main() { import std.stdio; A a; a.x[1] = 42; writeln(a.x); } Looks like a bona fide runtime array to me. AndreiTangentially related: since when we allow field initialization with new? I was surprised to see that this works:Since CTFE started supporting it... this might actually be an unintentional feature. Since the initializer is in a static context, the right hand side gets CTFE'd, which means that actually points to an array in the data segment... the *same* array in the data segment for all initializations (the pointer is just blitted over with the rest of init), which might be a bit surprising. I've seen a lot of people do this with classes not realizing it makes a static instance!
Oct 19 2015
On Monday, 19 October 2015 at 19:53:14 UTC, Andrei Alexandrescu wrote:struct A { int[] x = new int[10]; } void main() { import std.stdio; A a; a.x[1] = 42; writeln(a.x); } Looks like a bona fide runtime array to me.It is still in the static data segment. Try this: struct A { int[] x = new int[10]; } void main() { import std.stdio; A a; a.x[1] = 42; writeln(a.x); A a2; writeln(a2.x); assert(a.ptr is a2.ptr); // passes } The `new int[10]` is done at compile time and the pointer it produces is put into the .init for the struct. So the same pointer gets blitted over to ALL instances of `A`, meaning they alias the same data (until the slice gets reallocated by append or whatever).
Oct 19 2015
On 10/19/2015 09:57 PM, Adam D. Ruppe wrote:On Monday, 19 October 2015 at 19:53:14 UTC, Andrei Alexandrescu wrote:This is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }struct A { int[] x = new int[10]; } void main() { import std.stdio; A a; a.x[1] = 42; writeln(a.x); } Looks like a bona fide runtime array to me.It is still in the static data segment. Try this: struct A { int[] x = new int[10]; } void main() { import std.stdio; A a; a.x[1] = 42; writeln(a.x); A a2; writeln(a2.x); assert(a.ptr is a2.ptr); // passes } The `new int[10]` is done at compile time and the pointer it produces is put into the .init for the struct. So the same pointer gets blitted over to ALL instances of `A`, meaning they alias the same data (until the slice gets reallocated by append or whatever).
Oct 19 2015
On Monday, 19 October 2015 at 23:37:09 UTC, Timon Gehr wrote:This is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }Oooo. Ouch. Yeah, that pushes it from being a good idea to disallow this in order to avoid bugs to a necessity to disallow it in order to avoid breaking the type system. - Jonathan M Davis
Oct 19 2015
On Tuesday, 20 October 2015 at 01:49:08 UTC, Jonathan M Davis wrote:On Monday, 19 October 2015 at 23:37:09 UTC, Timon Gehr wrote:Yeah. But IMO it should still be allowed for immutable objects, and the error/deprecation message should say so, because for those it can actually be useful and there would be no other way to do it.This is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }Oooo. Ouch. Yeah, that pushes it from being a good idea to disallow this in order to avoid bugs to a necessity to disallow it in order to avoid breaking the type system.
Oct 20 2015
On Tuesday, 20 October 2015 at 11:36:36 UTC, Marc Schütz wrote:On Tuesday, 20 October 2015 at 01:49:08 UTC, Jonathan M Davis wrote:I believe that it's supposed to be possible to initialize an immutable member variable in a struct's constructor. So, the only thing that allowing member variables that are immutable objects or arrays to be directly initialized would add would be to make it so that they can be part of the init value. And in general, I'd strongly argue that it's bad practice to have either const or immutable member variables in a struct, because then it can never be assigned to even if the struct as a whole is supposed to be mutable. But that's a problem with member variables in general, not just those which refer to objects on the heap, so disallowing directly initializing with immutable objects/arrays on that basis wouldn't make sense IMHO. It just means that in most cases, it would be a bad idea for different reasons. What I _do_ think is useful is having a member variable which is Rebindable so that it refers to an immutable object but isn't actually immutable itself (SysTime is supposed to do that but can't at the moment due to a compiler bug). But regardless, having a member variable directly initialized with an immutable object or array should be safe and correct. So, I agree that we should allow it. const could be depending (though it's probably easier not to), but mutable definitely has to go. - Jonathan M DavisOn Monday, 19 October 2015 at 23:37:09 UTC, Timon Gehr wrote:Yeah. But IMO it should still be allowed for immutable objects, and the error/deprecation message should say so, because for those it can actually be useful and there would be no other way to do it.This is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }Oooo. Ouch. Yeah, that pushes it from being a good idea to disallow this in order to avoid bugs to a necessity to disallow it in order to avoid breaking the type system.
Oct 20 2015
On 10/19/15 9:49 PM, Jonathan M Davis wrote:On Monday, 19 October 2015 at 23:37:09 UTC, Timon Gehr wrote:Please file, thanks. -- AndreiThis is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }Oooo. Ouch. Yeah, that pushes it from being a good idea to disallow this in order to avoid bugs to a necessity to disallow it in order to avoid breaking the type system.
Oct 21 2015
On 10/21/2015 12:55 PM, Andrei Alexandrescu wrote:On 10/19/15 9:49 PM, Jonathan M Davis wrote:(I've filed it in 2013. https://issues.dlang.org/show_bug.cgi?id=10376 .) What should be the expected behaviour? - Don't allow default initialization with mutable indirections in aggregates. - Clone the mutable referenced data for each instance. - Have different "init" for different mutability. - ... ?On Monday, 19 October 2015 at 23:37:09 UTC, Timon Gehr wrote:Please file, thanks. -- AndreiThis is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }Oooo. Ouch. Yeah, that pushes it from being a good idea to disallow this in order to avoid bugs to a necessity to disallow it in order to avoid breaking the type system.
Oct 21 2015
On 10/21/2015 07:40 AM, Timon Gehr wrote:On 10/21/2015 12:55 PM, Andrei Alexandrescu wrote:The quickest way to stop the bleeding is to disallow the code. It's incorrect for immutable data and misleading for mutable data. (What an user might expect is that each data comes with a distinct array.) I can't believe I didn't know about this huge hole. AndreiOn 10/19/15 9:49 PM, Jonathan M Davis wrote:(I've filed it in 2013. https://issues.dlang.org/show_bug.cgi?id=10376 .) What should be the expected behaviour? - Don't allow default initialization with mutable indirections in aggregates. - Clone the mutable referenced data for each instance. - Have different "init" for different mutability. - ... ?On Monday, 19 October 2015 at 23:37:09 UTC, Timon Gehr wrote:Please file, thanks. -- AndreiThis is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }Oooo. Ouch. Yeah, that pushes it from being a good idea to disallow this in order to avoid bugs to a necessity to disallow it in order to avoid breaking the type system.
Oct 21 2015
On Wednesday, 21 October 2015 at 12:32:37 UTC, Andrei Alexandrescu wrote:On 10/21/2015 07:40 AM, Timon Gehr wrote:Fundamentally the problem is that literals of mutable reference types do not make sense. This is why I argued (before TDPL came out), that an explicit .dup should be required, rather than allowing the compiler to secretly add one automatically. Implicit conversion of an array literal to mutable is ridiculous IMHO. This is just one manifestation of the problem. It could be quite difficult to come up with a rule for what should be disallowed.On 10/21/2015 12:55 PM, Andrei Alexandrescu wrote:The quickest way to stop the bleeding is to disallow the code. It's incorrect for immutable data and misleading for mutable data. (What an user might expect is that each data comes with a distinct array.) I can't believe I didn't know about this huge hole. AndreiOn 10/19/15 9:49 PM, Jonathan M Davis wrote:(I've filed it in 2013. https://issues.dlang.org/show_bug.cgi?id=10376 .) What should be the expected behaviour? - Don't allow default initialization with mutable indirections in aggregates. - Clone the mutable referenced data for each instance. - Have different "init" for different mutability. - ... ?On Monday, 19 October 2015 at 23:37:09 UTC, Timon Gehr wrote:Please file, thanks. -- AndreiThis is the worst part: class C{ int[] x=[1,2,3]; } void main(){ auto mut=new C; auto imm=new immutable(C); assert(imm.x[0]==1); mut.x[0]=2; assert(imm.x[0]==2); }Oooo. Ouch. Yeah, that pushes it from being a good idea to disallow this in order to avoid bugs to a necessity to disallow it in order to avoid breaking the type system.
Oct 21 2015
On 10/21/2015 02:54 PM, Don wrote:Fundamentally the problem is that literals of mutable reference types do not make sense.I think considering "[x,y,z]" a 'literal' is a problem, but why is it the problem here? It is not really treated like a literal in this context. This has the same issue: class D{ int x=0; } class C{ auto x=new D; } void main(){ auto c1=new C; auto c2=new immutable(C); assert(c2.x.x==0); c1.x.x=1; assert(c2.x.x==1); }This is why I argued (before TDPL came out), that an explicit .dup should be required, rather than allowing the compiler to secretly add one automatically. Implicit conversion of an array literal to mutable is ridiculous IMHO.Where does the "implicit conversion to mutable" happen here?: class C{ int x; } void main(){ auto c=new C; auto a=[c]; }
Oct 21 2015
On Wednesday, 21 October 2015 at 12:32:37 UTC, Andrei Alexandrescu wrote:The quickest way to stop the bleeding is to disallow the code. It's incorrect for immutable data and misleading for mutable data. (What an user might expect is that each data comes with a distinct array.)It's perfectly correct for immutable data if the member variable is declared to be immutable or if it's a Rebindable which explicitly refers to immutable (in fact, SysTime would be defaulting to an immutable TimeZone for its default value if it weren't for the fact that a compiler bug is preventing it). What doesn't work is to have the member variable be declared to be mutable and then declare an instance of the object it's in to be immutable, since then you can have a mutable and immutable object referring to the same data with different levels of mutability. - Jonathan M Davis
Oct 21 2015
On Monday, 19 October 2015 at 18:16:15 UTC, Andrei Alexandrescu wrote:Tangentially related: since when we allow field initialization with new? I was surprised to see that this works:Thanks for this notice. I edited question on SO and I removed field initialization with new (replace class with struct). It is not related to my question and actually I wrote this code occasionally when I tried to reduce code to minimum working sample.
Oct 20 2015
On Monday, 19 October 2015 at 17:50:02 UTC, Nikolay wrote:On Monday, 19 October 2015 at 08:41:46 UTC, Jack Applegame wrote:DoneD template system is very powerful. This is more generic solution: http://dpaste.dzfl.pl/791c65d0e4eeWow! I can't believe that it is possible and there is so straightforward way. You should post this answer to SO Thanks!
Oct 19 2015
On 10/18/15 9:00 PM, David Osborne wrote:On Saturday, 17 October 2015 at 15:31:00 UTC, Nikolay wrote:I just came across a need for this, thanks. Is it me, or is this a bug? struct Foo { template opDispatch(string s) { // if you uncomment this, it compiles //void opDispatch() {} void opDispatch(T...)() {} } } void main() { Foo f; f.blue!char(); // Error: no property 'blue' for type 'Foo' } -SteveI asked on SO question about opDispatch and compile time parameters: http://stackoverflow.com/questions/32998781/opdispatch-and-compile-time-parameters [...] Is it good idea for opDispatch improvement or may there is some other approach?I must have my answer not too long after you made this thread, but there is another approach: (copied from SO) You need to use the eponymous template pattern, and have an opDispatch function with your compile-time parameters inside an outer opDispatch template that takes the regular opDispatch string parameter. You can also have multiple inner opDispatch functions (and fields) that follow regular overload rules. import std.stdio; struct Foo { public template opDispatch(string name) { public string opDispatch() { return name; } public T opDispatch(T)() { return T.init; } public string opDispatch(T)(string s) { return name ~ ' ' ~ T.stringof ~ ' ' ~ s; } } } void main() { Foo foo; writeln( foo.bar ); writeln( foo.baz!int ); writeln( foo.foo!Foo("foo") ); } Produces the output: bar 0 foo Foo foo DPaste link: http://dpaste.dzfl.pl/6e5cfca8b702 I haven't fully explored the limitations of this approach, but it does work for simple cases.
Nov 13 2015
On Saturday, 14 November 2015 at 04:10:59 UTC, Steven Schveighoffer wrote:Is it me, or is this a bug? struct Foo { template opDispatch(string s) { // if you uncomment this, it compiles //void opDispatch() {} void opDispatch(T...)() {} } } void main() { Foo f; f.blue!char(); // Error: no property 'blue' for type 'Foo' } -SteveUsually you should explicitly instantiate opDispatch to see the real error message(s) that prevented opDispatch from being used, but in this case I can't find a way to do that. `f.opDispatch!("blue")!char()` is syntactically invalid, and aliasing doesn't work either: `alias t = f.opDispatch!"blue"; t!char(); // Error: need `this` for opDispatch`. I wonder what the real error is.
Nov 13 2015
On 11/14/15 12:18 AM, Jakob Ovrum wrote:On Saturday, 14 November 2015 at 04:10:59 UTC, Steven Schveighoffer wrote:It appears that you can't directly access the eponymous member, this doesn't work: f.opDispatch!("blue").opDispatch!(char)(); Error: no property 'opDispatch' for type 'void' Perhaps that fact that it *works* when you have the non-template version as the first member is the bug ;) In any case, I have used Jack Applegame's improved version, and it works beautifully! -SteveIs it me, or is this a bug? struct Foo { template opDispatch(string s) { // if you uncomment this, it compiles //void opDispatch() {} void opDispatch(T...)() {} } } void main() { Foo f; f.blue!char(); // Error: no property 'blue' for type 'Foo' }Usually you should explicitly instantiate opDispatch to see the real error message(s) that prevented opDispatch from being used, but in this case I can't find a way to do that. `f.opDispatch!("blue")!char()` is syntactically invalid, and aliasing doesn't work either: `alias t = f.opDispatch!"blue"; t!char(); // Error: need `this` for opDispatch`. I wonder what the real error is.
Nov 16 2015