www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Debug help - opDispatch - unknown member function

reply Brother Bill <brotherbill mail.com> writes:
https://tour.dlang.org/tour/en/gems/opdispatch-opapply

This states: "Any unknown member function call to that type is 
passed to opDispatch, passing the unknown member function's name 
as a string template parameter."

So I tried that.  But the compiler didn't like it.
How should I play the game of passing in an unknown member 
function name?

```
import std.stdio;

void main()
{
	CallLogger!C l;
	l.callA(1, 2);
	l.callB("ABC");
	l.callHome("foo", "bar");   // Fails here
}

struct C
{
	void callA(int i, int j)
	{
	}

	void callB(string s)
	{

	}
}

struct CallLogger(C)
{
	C content;
	void opDispatch(string name, T...)(T vals)
	{
		writeln("called ", name);
		mixin("content." ~ name)(vals);
	}
}

```

Console output:
```
c:\dev\D\D_templates_tutorial\toy1\source\app.d(8): Error: no 
property `callHome` for `l` of type `app.CallLogger!(C)`
     l.callHome("foo", "bar");
      ^
```
Sep 08
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 9 September 2025 at 00:40:31 UTC, Brother Bill wrote:
 https://tour.dlang.org/tour/en/gems/opdispatch-opapply

 This states: "Any unknown member function call to that type is 
 passed to opDispatch, passing the unknown member function's 
 name as a string template parameter."

 [...]
you didnt define "callhome" so the the mixin fails, this propagates a "__error" or something, and opDispatch when it contains an error doesnt pass it up the stack
Sep 08
parent reply Brother Bill <brotherbill mail.com> writes:
On Tuesday, 9 September 2025 at 00:45:00 UTC, monkyyy wrote:
 On Tuesday, 9 September 2025 at 00:40:31 UTC, Brother Bill 
 wrote:
 https://tour.dlang.org/tour/en/gems/opdispatch-opapply

 This states: "Any unknown member function call to that type is 
 passed to opDispatch, passing the unknown member function's 
 name as a string template parameter."

 [...]
you didnt define "callhome" so the the mixin fails, this propagates a "__error" or something, and opDispatch when it contains an error doesnt pass it up the stack
Then I'm not clear what an "unknown" member function name means. Kindly provide an example.
Sep 08
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 9 September 2025 at 01:08:41 UTC, Brother Bill wrote:
 On Tuesday, 9 September 2025 at 00:45:00 UTC, monkyyy wrote:
 On Tuesday, 9 September 2025 at 00:40:31 UTC, Brother Bill 
 wrote:
 https://tour.dlang.org/tour/en/gems/opdispatch-opapply

 This states: "Any unknown member function call to that type 
 is passed to opDispatch, passing the unknown member 
 function's name as a string template parameter."

 [...]
you didnt define "callhome" so the the mixin fails, this propagates a "__error" or something, and opDispatch when it contains an error doesnt pass it up the stack
Then I'm not clear what an "unknown" member function name means. Kindly provide an example.
```d import std; struct Vector(int N,T,string fields){ static foreach(I;0..N){ mixin("T "~fields[I]~";"); } auto opDispatch(string __s__)(){//I suggest a habit of avoiding simple names when generating mixin code return mixin((){ string output="tuple("; foreach(C;__s__){ output~=C; output~=','; } output~=")"; return output; }()); } } unittest{ Vector!(3,int,"xyz") foo; foo.x=5; foo.y=3; foo.yx.writeln; Vector!(4,ubyte,"rgba") bar; bar.bgr.writeln; } ```
Sep 08
next sibling parent Brother Bill <brotherbill mail.com> writes:
On Tuesday, 9 September 2025 at 01:24:59 UTC, monkyyy wrote:
 ```d
 import std;
 struct Vector(int N,T,string fields){
 	static foreach(I;0..N){
 		mixin("T "~fields[I]~";");
 	}
 	auto opDispatch(string __s__)(){//I suggest a habit of 
 avoiding simple names when generating mixin code
 		return mixin((){
 			string output="tuple(";
 			foreach(C;__s__){
 				output~=C;
 				output~=',';
 			}
 			output~=")";
 			return output;
 		}());
 	}
 }
 unittest{
 	Vector!(3,int,"xyz") foo;
 	foo.x=5;
 	foo.y=3;
 	foo.yx.writeln;
 	Vector!(4,ubyte,"rgba") bar;
 	bar.bgr.writeln;
 }
 ```
I simplified your example, and manually expanded opDispatch so D newbies can grok it. Please explain the opDispatch IIFE and why no semicolon needed at the end. ``` import std; // Comment out yx() xor opDispatch() struct Vector(int N, T, string fields){ int x; int y; int z; // manual expansion of opDispatch // auto yx() { // string output = "tuple("; // foreach (char scv; "yx") { // output ~= scv; // output ~= ","; // } // output ~= ")"; // writeln(output); // This is not in opDispatch() // return tuple(3, 5); // Hack to return same result // } // I suggest a habit of avoiding simple names when generating mixin code auto opDispatch(string __s__)() { // IIFE writeln("__s__: ", __s__); return mixin(() { string output = "tuple("; foreach (C; __s__){ output ~= C; output ~= ','; } output ~= ")"; return output; } ()); } } void main() { Vector!(3, int, "xyz") foo; foo.x = 5; // Standard assignment to known member foo.y = 3; // Standard assignment to known member foo.yx.writeln; // yx not a member of Vector, so opDispatch is called }} ``` Console output: ``` __s__: yx Tuple!(int, int)(3, 5) ```
Sep 09
prev sibling parent reply Brother Bill <brotherbill mail.com> writes:
On Tuesday, 9 September 2025 at 01:24:59 UTC, monkyyy wrote:
 ```
 // I suggest a habit of avoiding simple names when generating 
 mixin code
 ```
Please provide an example where providing simple names causes 'trouble'.
Sep 09
parent monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 9 September 2025 at 11:57:54 UTC, Brother Bill wrote:
 On Tuesday, 9 September 2025 at 01:24:59 UTC, monkyyy wrote:
 ```
 // I suggest a habit of avoiding simple names when generating 
 mixin code
 ```
Please provide an example where providing simple names causes 'trouble'.
```d import std; struct Vector(int N,T,string fields){ static foreach(I;0..N){ mixin("T "~fields[I]~";"); } auto opDispatch(string s)(){//I suggest a habit of avoiding simple names when generating mixin code return mixin((){ string output="tuple("; foreach(C;s){ output~=C; output~=','; } output~=")"; return output; }()); } } unittest{ Vector!(4,int,"xyzs") foo; foo.x=5; foo.y=3; foo.s=7; assert(foo.sx==tuple(7,5)); } ```
Sep 09
prev sibling next sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Tuesday, 9 September 2025 at 00:40:31 UTC, Brother Bill wrote:
 https://tour.dlang.org/tour/en/gems/opdispatch-opapply

 This states: "Any unknown member function call to that type is 
 passed to opDispatch, passing the unknown member function's 
 name as a string template parameter."

 So I tried that.  But the compiler didn't like it.
 How should I play the game of passing in an unknown member 
 function name?
Hopefully this slightly modified, and commented version of your original code will help you understand why your mixin is failing: ```d import std.stdio; void main() { CallLogger!C l; l.callA(1, 2); l.callB("ABC"); // CallLogger does not have method called "callHome", but it has opDispatch, so // the compiler will lower this down to l.opDispatch("callHome", "foo", "bar") l.callHome("foo", "bar"); } struct C { void callA(int i, int j) { } void callB(string s) { } // Your mixin generates code that calls C.callHome which did not exist void callHome(string a, string b) { writeln(a, "/", b); } } struct CallLogger(C) { C content; void opDispatch(string name, T...)(T vals) { writeln("called ", name); // Now it works, because C now has callHome method mixin("content." ~ name)(vals); } } ```
Sep 09
parent reply Brother Bill <brotherbill mail.com> writes:
On Tuesday, 9 September 2025 at 09:59:40 UTC, Dejan Lekic wrote:
 Hopefully this slightly modified, and commented version of your 
 original code will
 help you understand why your mixin is failing:

 ```d
 import std.stdio;

 void main() {
     CallLogger!C l;
     l.callA(1, 2);
     l.callB("ABC");

     // CallLogger does not have method called "callHome", but 
 it has opDispatch, so
     // the compiler will lower this down to 
 l.opDispatch("callHome", "foo", "bar")
     l.callHome("foo", "bar");
 }

 struct C {
     void callA(int i, int j) {
     }

     void callB(string s) {
     }

     // Your mixin generates code that calls C.callHome which 
 did not exist
     void callHome(string a, string b) {
         writeln(a, "/", b);
     }
 }

 struct CallLogger(C) {
     C content;
     void opDispatch(string name, T...)(T vals) {
         writeln("called ", name);

         // Now it works, because C now has callHome method
         mixin("content." ~ name)(vals);
     }
 }
 ```
When commenting out the callHome() in struct C, it fails. Obviously if callHome() is explicitly created, it works. ``` import std.stdio; void main() { CallLogger!C l; l.callA(1, 2); l.callB("ABC"); // CallLogger does not have method called "callHome", but it has opDispatch, so // the compiler will lower this down to l.opDispatch("callHome", "foo", "bar") l.callHome("foo", "bar"); } struct C { void callA(int i, int j) { } void callB(string s) { } // Your mixin generates code that calls C.callHome which did not exist // void callHome(string a, string b) { // writeln(a, "/", b); // } } struct CallLogger(C) { C content; void opDispatch(string name, T...)(T vals) { writeln("called ", name); // Now it works, because C now has callHome method mixin("content." ~ name)(vals); } } ``` Console contents: ``` c:\dev\D\D_templates_tutorial\toy1\source\app.d(10): Error: no property `callHome` for `l` of type `app.CallLogger!(C)` l.callHome("foo", "bar"); ^ ```
Sep 09
next sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
On Tuesday, 9 September 2025 at 12:04:07 UTC, Brother Bill wrote:
 When commenting out the callHome() in struct C, it fails.
 Obviously if callHome() is explicitly created, it works.
If C does not have callHome() method, then ```d mixin("content." ~ name)(vals); ``` will simply fail, because that line effectively generates this code: ```d content.callHome("foo", "bar"); ``` Since content is of type C, which does not have callHome method, it will fail, and is expected to fail. Sorry I can't think of a better explanation than what I tried in my previous post... Since I can't come up with a better explanation I will stop writing in this thread. Good luck!
Sep 09
prev sibling parent IchorDev <zxinsworld gmail.com> writes:
On Tuesday, 9 September 2025 at 12:04:07 UTC, Brother Bill wrote:
 When commenting out the callHome() in struct C, it fails.
You can't call a function that doesn't exist. When the function doesn't exist, you can't call it.
 Obviously if callHome() is explicitly created, it works.
*Explicitly* created? You can't just call a non-existent function and expect the compiler to generate it for you. You would need to have an `opDispatch` in `struct C` in order to do what you seem to be expecting to happen.
Sep 09
prev sibling next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 9 September 2025 at 00:40:31 UTC, Brother Bill wrote:
 https://tour.dlang.org/tour/en/gems/opdispatch-opapply

 This states: "Any unknown member function call to that type is 
 passed to opDispatch, passing the unknown member function's 
 name as a string template parameter."
Specs are often declarative but the compiler(especially walters compiler) and the machine it run on is imperative It should say "if overload resolution inside a struct is failing, try a hack where an opDispatch is called"
Sep 09
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Tuesday, 9 September 2025 at 00:40:31 UTC, Brother Bill wrote:

 ```
 c:\dev\D\D_templates_tutorial\toy1\source\app.d(8): Error: no 
 property `callHome` for `l` of type `app.CallLogger!(C)`
     l.callHome("foo", "bar");
      ^
 ```
You might be expecting `opDispatch` inside `CallLogger` to try and build `opDispatch!("callHome")("foo", "bar")`. The thing is, it does. But when the compiler encounters failures, it means that it assumes `opDispatch` was not designed to handle that function, and it moves on to other options (UFCS maybe). This is a long standing issue, and one that is very difficult to fix due to the way name lookup rules are working in the compiler. In short `opDispatch` only is valid if it compiles. If it doesn't compile, it's as if it doesn't exist. This limitation is likely with us until someone can rework how the name lookups and IFTI works. -Steve
Sep 09
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 9 September 2025 at 19:17:11 UTC, Steven 
Schveighoffer wrote:
 
 In short `opDispatch` only is valid if it compiles. If it 
 doesn't compile, it's as if it doesn't exist.
Yours still thinking declaratively, that cant be true. The best description of its behavior is that it discards errors. ```d import std; enum counter=cast(immutable(void)*)[0].ptr; auto getcount()=>(*(cast(int*)counter)); auto count()=>(*(cast(int*)counter))++; struct foo{ void opDispatch(string s)(){ enum _=count(); static assert(0); } } void bar(foo){} void foobar(foo){} unittest{ foo().bar; foo().foobar; getcount.writeln;//2 } ```
Sep 09
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Tuesday, 9 September 2025 at 20:08:06 UTC, monkyyy wrote:
 On Tuesday, 9 September 2025 at 19:17:11 UTC, Steven 
 Schveighoffer wrote:
 
 In short `opDispatch` only is valid if it compiles. If it 
 doesn't compile, it's as if it doesn't exist.
Yours still thinking declaratively, that cant be true. The best description of its behavior is that it discards errors. ```d import std; enum counter=cast(immutable(void)*)[0].ptr; auto getcount()=>(*(cast(int*)counter)); auto count()=>(*(cast(int*)counter))++; struct foo{ void opDispatch(string s)(){ enum _=count(); static assert(0); } } void bar(foo){} void foobar(foo){} unittest{ foo().bar; foo().foobar; getcount.writeln;//2 } ```
This calls the UFCS versions, because the `opDispatch` version does not compile. The other thing is just a bug. -Steve
Sep 09
parent monkyyy <crazymonkyyy gmail.com> writes:
On Tuesday, 9 September 2025 at 20:56:19 UTC, Steven 
Schveighoffer wrote:
 On Tuesday, 9 September 2025 at 20:08:06 UTC, monkyyy wrote:
 On Tuesday, 9 September 2025 at 19:17:11 UTC, Steven 
 Schveighoffer wrote:
 
 In short `opDispatch` only is valid if it compiles. If it 
 doesn't compile, it's as if it doesn't exist.
Yours still thinking declaratively, that cant be true. The best description of its behavior is that it discards errors. ```d import std; enum counter=cast(immutable(void)*)[0].ptr; auto getcount()=>(*(cast(int*)counter)); auto count()=>(*(cast(int*)counter))++; struct foo{ void opDispatch(string s)(){ enum _=count(); static assert(0); } } void bar(foo){} void foobar(foo){} unittest{ foo().bar; foo().foobar; getcount.writeln;//2 } ```
This calls the UFCS versions, because the `opDispatch` version does not compile. The other thing is just a bug. -Steve
```d import std; struct foo{ void opDispatch(string s)(){ pragma(msg,"hi"); static assert(0); } } void bar(foo){} unittest{ foo().bar;//prints hi } ``` Even if you were egregious bigoted to some features out of some kind of spec purity belief; its demonstrable with entirely in spec behavior
Sep 11