digitalmars.D.learn - advanced function binding
- Moritz Warning (18/18) Mar 27 2007 Hi,
- Kirk McDonald (33/56) Mar 28 2007 This problem is highly analogous to how Pyd exposes D functions to
- Moritz Warning (63/64) Mar 28 2007 Hi,
- Kirk McDonald (53/126) Mar 28 2007 D distinguishes between function types and function pointer types. That
- Moritz Warning (15/79) Mar 28 2007 Hi,
- Kirk McDonald (15/34) Mar 29 2007 Oh! I've seen this before. When you take the address of a member
- Moritz Warning (7/26) Mar 29 2007 dg.funcptr = cast(FuncPtr) &Func;
- Moritz Warning (24/24) Mar 30 2007 After some additional input by Kirk (thanks!), this come up for solution...
- Jason House (3/30) Apr 16 2007 Out of curiosity, not have call accept a delegate instead of a class?
- Jason House (4/27) Mar 29 2007 Wow, this sounds exactly like a set of C++ code that I plan to convert
Hi, I try to write a RPC interface in D and have problems to start. I've already written the code in C++, but it became quite unmanageable and ugly because I had to write various workarounds. The core problem is to hide an arbitrary (member) function pointer in a class. The argument values are provided _later_ in the form of a char[][] to a class function: void setArgs(char[][] args) The wrapped function is called later by a class function that should rely on two template parameters only; the type of object the function will be called on, and the return type: R call(R,T)(T obj) { /**/ } For the type conversion I have several global T convert(T)(char[]) functions. I've read about std.bind and recursive templates but wasn't able to make much use of them. I have thought about to pass a lazy template delegate (with convert(T)(char[]) inside) to std.bind, but that's apparently not possible Maybe somebody can give me a push into the right direction. :-) .
Mar 27 2007
Moritz Warning wrote:Hi, I try to write a RPC interface in D and have problems to start. I've already written the code in C++, but it became quite unmanageable and ugly because I had to write various workarounds. The core problem is to hide an arbitrary (member) function pointer in a class. The argument values are provided _later_ in the form of a char[][] to a class function: void setArgs(char[][] args) The wrapped function is called later by a class function that should rely on two template parameters only; the type of object the function will be called on, and the return type: R call(R,T)(T obj) { /**/ } For the type conversion I have several global T convert(T)(char[]) functions. I've read about std.bind and recursive templates but wasn't able to make much use of them. I have thought about to pass a lazy template delegate (with convert(T)(char[]) inside) to std.bind, but that's apparently not possible Maybe somebody can give me a push into the right direction. :-) .This problem is highly analogous to how Pyd exposes D functions to Python. (Only instead of doing a char[] -> T conversion, Pyd does a PyObject* -> T conversion.) There are two issues involved here: First, the class is storing a pointer to a member function, a concept which D does not directly support. Instead, D has the generally more useful concept of delegates, which is a fat pointer combining the pointer to the member function and an object reference into a single package. While this is usually more useful, it can get in the way of writing a dispatch mechanism like you seem to want (and which Pyd uses to implement class wrapping). Luckily, you can access the internals of a delegate directly to get around this: class Foo { void foo() {} } { void delegate() dg; dg.funcptr = &Foo.foo; dg.ptr = new Foo; dg(); } Second is actually calling the function with the provided arguments. To explain how Pyd does it, I shall dust off this ancient pastebin, from when tuples were first added to the language: http://paste.dprogramming.com/dpbaqugv.php That paste is a rough outline of how Pyd's function wrapping works, and I think it is exactly what you are looking for. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Mar 28 2007
http://paste.dprogramming.com/dpbaqugv.phpHi, thank you for your response! It was very helpful indeed. Here is the example code I'm experimenting on: import std.traits; import std.conv; import std.bind; void main() { Wrapper!(A, Foo.getBar) x = new Wrapper!(A, Foo.getBar)(Foo.getBar); } class Foo { void getBar() {} } class Bar { } class Wrapper(B, alias Func) { alias ReturnType!(Func) R; alias typeof(Func) * FuncPtr; alias typeof(ReturnType!(std.bind.bindAlias!(Func))) BindPtr; //type is DerefFunc!(R) or void? FuncPtr funcptr; BindPtr bindptr; //1. store pointer this(FuncPtr funcptr) { this.funcptr = funcptr; } //2. bind parameters void bind(char[][] u) { ParameterTypeTuple!(Func) t; foreach (i, arg; t) { t[i] = convert!(typeof(arg))(u[i]); } bindptr = std.bind.bindAlias!(Func)(t); //fn(t); } //3. call function on object R call(B b) { R delegate() dg; dg.funcptr = this.funcptr; //need to assign bindptr! dg.ptr = cast(void*) b; return dg(); } } T convert(T)(char[] u) { static if (is(T == int)) { return std.conv.toInt(u); } else static if (is(T == float)) { return std.conv.toFloat(u);; } else static if (is(T == char[])) { return u; } else static assert(false, "Unsupported type: " ~ T.stringof); } It doesn't compile because I cannot assign a bindAlias!(Func)(t) to bindptr. The compiler tells me the return type of bindAlias is void, when I look at the source I would guess it's DerefFunc!(R). Anyway, somehow I must be able to store the binded function and assign it to dg.funcptr in call(). What is going wrong?
Mar 28 2007
Moritz Warning wrote:D distinguishes between function types and function pointer types. That is, given this: void foo() {} The following are different types: typeof(foo) typeof(&foo) The former is a function type, and is not actually that useful. (You can't declare variables of this type, or use it as a function parameter or return type.) The latter is a function pointer, and is very useful. In that example, it is equivalent to the type "void function()". If you want to get a pointer to a function in D, you MUST use the address-of operator (as in &foo). Thus, the line: alias typeof(Func) * FuncPtr; should actually be: alias typeof(&Func) FuncPtr; You are passing the function to the class as both an alias template argument and a constructor argument. It is then available both as a template argument, and as a member variable. This seems redundant. Supplying it as a template argument is probably preferable. Furthermore, experience has shown me it is wise to allow the user to explicitly specify the type of the function. Thus, FuncPtr should actually be a third template parameter, like this: class Wrapper(B, alias Func, FuncPtr = typeof(&Func)) {} You can then dispense with the constructor and the funcptr member variable entirely. Next, do you really need to store the /converted/ arguments? Why not just store the char[][], and convert the arguments when the function is actually called? The class looks like this if you do that: class Wrapper(B, alias Func, FuncPtr = typeof(&Func) { alias ReturnType!(FuncPtr) R; alias ParameterTypeTuple!(FuncPtr) Params; char[][] args; void bind(char[][] u) { args = u; } R call(B b) { R delegate(Params) dg; dg.funcptr = &Func; dg.ptr = cast(void*)b; Params t; foreach (i, arg; t) { t[i] = convert!(typeof(arg))(this.args[i]); } return dg(t); } } Note that std.bind is not needed at all if it is done this way. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orghttp://paste.dprogramming.com/dpbaqugv.phpHi, thank you for your response! It was very helpful indeed. Here is the example code I'm experimenting on: import std.traits; import std.conv; import std.bind; void main() { Wrapper!(A, Foo.getBar) x = new Wrapper!(A, Foo.getBar)(Foo.getBar); } class Foo { void getBar() {} } class Bar { } class Wrapper(B, alias Func) { alias ReturnType!(Func) R; alias typeof(Func) * FuncPtr; alias typeof(ReturnType!(std.bind.bindAlias!(Func))) BindPtr; //type is DerefFunc!(R) or void? FuncPtr funcptr; BindPtr bindptr; //1. store pointer this(FuncPtr funcptr) { this.funcptr = funcptr; } //2. bind parameters void bind(char[][] u) { ParameterTypeTuple!(Func) t; foreach (i, arg; t) { t[i] = convert!(typeof(arg))(u[i]); } bindptr = std.bind.bindAlias!(Func)(t); //fn(t); } //3. call function on object R call(B b) { R delegate() dg; dg.funcptr = this.funcptr; //need to assign bindptr! dg.ptr = cast(void*) b; return dg(); } } T convert(T)(char[] u) { static if (is(T == int)) { return std.conv.toInt(u); } else static if (is(T == float)) { return std.conv.toFloat(u);; } else static if (is(T == char[])) { return u; } else static assert(false, "Unsupported type: " ~ T.stringof); } It doesn't compile because I cannot assign a bindAlias!(Func)(t) to bindptr. The compiler tells me the return type of bindAlias is void, when I look at the source I would guess it's DerefFunc!(R). Anyway, somehow I must be able to store the binded function and assign it to dg.funcptr in call(). What is going wrong?
Mar 28 2007
Hi, thank you again! I wasn't aware that the delegate can accept a ParameterTypeTuple and the difference between the function type declarations. Something I have learned, nice. :) Anyway, one probably small problem remains; when I try to compile your code I get: Main.d:94: Error: this for getBar needs to be type Foo not type Main.Wrapper!(Foo,getBar).Wrapper Main.d:94: Error: cannot implicitly convert expression (&this.getBar) of type void delegate(()) to void(*)() Main.d:65: template instance Main.Wrapper!(Foo,getBar) error instantiating line 94 is "dg.funcptr = &Func;" line 65 is "Wrapper!(Foo, Foo.getBar) x = new Wrapper!(Foo, Foo.getBar)();" It seems to me the compiler assumes the base class of &Func is not Foo, but the surrounding object?! - weird. I got rid of the second error adding cast(void(*)()), but it also leaves a bad feeling. :P Kirk McDonald Wrote:D distinguishes between function types and function pointer types. That is, given this: void foo() {} The following are different types: typeof(foo) typeof(&foo) The former is a function type, and is not actually that useful. (You can't declare variables of this type, or use it as a function parameter or return type.) The latter is a function pointer, and is very useful. In that example, it is equivalent to the type "void function()". If you want to get a pointer to a function in D, you MUST use the address-of operator (as in &foo). Thus, the line: alias typeof(Func) * FuncPtr; should actually be: alias typeof(&Func) FuncPtr; You are passing the function to the class as both an alias template argument and a constructor argument. It is then available both as a template argument, and as a member variable. This seems redundant. Supplying it as a template argument is probably preferable. Furthermore, experience has shown me it is wise to allow the user to explicitly specify the type of the function. Thus, FuncPtr should actually be a third template parameter, like this:Same experience to me using C++. :)class Wrapper(B, alias Func, FuncPtr = typeof(&Func)) {} You can then dispense with the constructor and the funcptr member variable entirely. Next, do you really need to store the /converted/ arguments? Why not just store the char[][], and convert the arguments when the function is actually called? The class looks like this if you do that: class Wrapper(B, alias Func, FuncPtr = typeof(&Func) { alias ReturnType!(FuncPtr) R; alias ParameterTypeTuple!(FuncPtr) Params; char[][] args; void bind(char[][] u) { args = u; } R call(B b) { R delegate(Params) dg; dg.funcptr = &Func; dg.ptr = cast(void*)b; Params t; foreach (i, arg; t) { t[i] = convert!(typeof(arg))(this.args[i]); } return dg(t); } } Note that std.bind is not needed at all if it is done this way. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Mar 28 2007
Moritz Warning wrote:Hi, thank you again! I wasn't aware that the delegate can accept a ParameterTypeTuple and the difference between the function type declarations. Something I have learned, nice. :) Anyway, one probably small problem remains; when I try to compile your code I get: Main.d:94: Error: this for getBar needs to be type Foo not type Main.Wrapper!(Foo,getBar).Wrapper Main.d:94: Error: cannot implicitly convert expression (&this.getBar) of type void delegate(()) to void(*)() Main.d:65: template instance Main.Wrapper!(Foo,getBar) error instantiating line 94 is "dg.funcptr = &Func;" line 65 is "Wrapper!(Foo, Foo.getBar) x = new Wrapper!(Foo, Foo.getBar)();" It seems to me the compiler assumes the base class of &Func is not Foo, but the surrounding object?! - weird. I got rid of the second error adding cast(void(*)()), but it also leaves a bad feeling. :POh! I've seen this before. When you take the address of a member function, any member function, even one of an unrelated class, from inside a class, the compiler wants to take it as a delegate with 'this' as the context. It then complains that 'this' is of the wrong type, as you see in the first error. For a better feeling, try cast(FuncPtr) instead. One other thing: You can re-write that assignment using D's type inference. You can also drop the trailing parentheses. auto x = new Wrapper!(Foo, Foo.getBar); Isn't that much better? :-) -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Mar 29 2007
dg.funcptr = cast(FuncPtr) &Func; The compiler resists the cast and gives an error again. So I get this error: Main.d:104: Error: this for getBar needs to be type Foo not type Main.Wrapper!(Foo,getBar).Wrapper Main.d:65: template instance Main.Wrapper!(Foo,getBar) error instantiating Kirk McDonald Wrote:Oh! I've seen this before. When you take the address of a member function, any member function, even one of an unrelated class, from inside a class, the compiler wants to take it as a delegate with 'this' as the context. It then complains that 'this' is of the wrong type, as you see in the first error. For a better feeling, try cast(FuncPtr) instead. One other thing: You can re-write that assignment using D's type inference. You can also drop the trailing parentheses. auto x = new Wrapper!(Foo, Foo.getBar); Isn't that much better? :-)Yes, it is! =)-- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Mar 29 2007
After some additional input by Kirk (thanks!), this come up for solution: class Wrapper(B, alias Func, FuncPtr = typeof(&Func) { alias ReturnType!(FuncPtr) R; alias ParameterTypeTuple!(FuncPtr) Params; char[][] args; void bind(char[][] u) { args = u; } private static FuncPtr get_funcptr() { return &Func; } R call(B b) { R delegate(Params) dg; dg.funcptr = get_funcptr(); dg.ptr = cast(void*)b; Params t; foreach (i, arg; t) { t[i] = convert!(typeof(arg))(this.args[i]); } return dg(t); } } Thread.join() // ;-)
Mar 30 2007
Out of curiosity, not have call accept a delegate instead of a class? That would make wrapper independent of the specific class (B)... Moritz Warning wrote:After some additional input by Kirk (thanks!), this come up for solution: class Wrapper(B, alias Func, FuncPtr = typeof(&Func) { alias ReturnType!(FuncPtr) R; alias ParameterTypeTuple!(FuncPtr) Params; char[][] args; void bind(char[][] u) { args = u; } private static FuncPtr get_funcptr() { return &Func; } R call(B b) { R delegate(Params) dg; dg.funcptr = get_funcptr(); dg.ptr = cast(void*)b; Params t; foreach (i, arg; t) { t[i] = convert!(typeof(arg))(this.args[i]); } return dg(t); } } Thread.join() // ;-)
Apr 16 2007
Wow, this sounds exactly like a set of C++ code that I plan to convert to D in the near future. I look forward to seeing your final resolution to this problem. Moritz Warning wrote:Hi, I try to write a RPC interface in D and have problems to start. I've already written the code in C++, but it became quite unmanageable and ugly because I had to write various workarounds. The core problem is to hide an arbitrary (member) function pointer in a class. The argument values are provided _later_ in the form of a char[][] to a class function: void setArgs(char[][] args) The wrapped function is called later by a class function that should rely on two template parameters only; the type of object the function will be called on, and the return type: R call(R,T)(T obj) { /**/ } For the type conversion I have several global T convert(T)(char[]) functions. I've read about std.bind and recursive templates but wasn't able to make much use of them. I have thought about to pass a lazy template delegate (with convert(T)(char[]) inside) to std.bind, but that's apparently not possible Maybe somebody can give me a push into the right direction. :-) .
Mar 29 2007