www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opDispatch and compile time parameters

reply Nikolay <sibnick gmail.com> writes:
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
next sibling parent Meta <jared771 gmail.com> writes:
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
prev sibling parent reply David Osborne <krendilboove gmail.com> writes:
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
next sibling parent reply Jack Applegame <japplegame gmail.com> writes:
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
parent reply Nikolay <sibnick gmail.com> writes:
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/791c65d0e4ee
Wow! 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
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/19/2015 01:50 PM, Nikolay wrote:
 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/791c65d0e4ee
Wow! I can't believe that it is possible and there is so straightforward way. You should post this answer to SO Thanks!
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). Andrei
Oct 19 2015
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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:
 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!
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 Davis
Oct 19 2015
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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:
 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!
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.
Urgh. That looks like quite a bit of a bummer. -- Andrei
Oct 19 2015
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/19/2015 02:26 PM, Adam D. Ruppe wrote:
 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!
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. Andrei
Oct 19 2015
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/19/2015 09:57 PM, Adam D. Ruppe wrote:
 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).
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); }
Oct 19 2015
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
next sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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:
 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.
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.
Oct 20 2015
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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:
 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.
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.
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 Davis
Oct 20 2015
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/19/15 9:49 PM, Jonathan M Davis wrote:
 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.
Please file, thanks. -- Andrei
Oct 21 2015
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/21/2015 12:55 PM, Andrei Alexandrescu wrote:
 On 10/19/15 9:49 PM, Jonathan M Davis wrote:
 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.
Please file, thanks. -- Andrei
(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. - ... ?
Oct 21 2015
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/21/2015 07:40 AM, Timon Gehr wrote:
 On 10/21/2015 12:55 PM, Andrei Alexandrescu wrote:
 On 10/19/15 9:49 PM, Jonathan M Davis wrote:
 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.
Please file, thanks. -- Andrei
(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. - ... ?
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. Andrei
Oct 21 2015
next sibling parent reply Don <prosthetictelevisions teletubby.medical.com> writes:
On Wednesday, 21 October 2015 at 12:32:37 UTC, Andrei 
Alexandrescu wrote:
 On 10/21/2015 07:40 AM, Timon Gehr wrote:
 On 10/21/2015 12:55 PM, Andrei Alexandrescu wrote:
 On 10/19/15 9:49 PM, Jonathan M Davis wrote:
 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.
Please file, thanks. -- Andrei
(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. - ... ?
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. Andrei
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.
Oct 21 2015
parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling parent Nikolay <sibnick gmail.com> writes:
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
prev sibling parent Jack Applegame <japplegame gmail.com> writes:
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:
 D template system is very powerful.  This is more generic 
 solution:

 http://dpaste.dzfl.pl/791c65d0e4ee
Wow! I can't believe that it is possible and there is so straightforward way. You should post this answer to SO Thanks!
Done
Oct 19 2015
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/18/15 9:00 PM, David Osborne wrote:
 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.
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' } -Steve
Nov 13 2015
parent reply Jakob Ovrum <jakobovrum gmail.com> writes:
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'
 }

 -Steve
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 13 2015
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/14/15 12:18 AM, Jakob Ovrum wrote:
 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'
 }
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.
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! -Steve
Nov 16 2015