Thread overview | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
March 28, 2007 advanced function binding | ||||
---|---|---|---|---|
| ||||
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. :-) . |
March 28, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Warning | 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 |
March 28, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kirk McDonald | > http://paste.dprogramming.com/dpbaqugv.php Hi, 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? |
March 28, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Warning | Moritz Warning wrote: >>http://paste.dprogramming.com/dpbaqugv.php > > > Hi, > > 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? 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.org |
March 29, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kirk McDonald | 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 |
March 29, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Warning | 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. :P > 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? :-) -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org |
March 29, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kirk McDonald | 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 |
March 30, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Warning | 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. :-)
> .
|
March 31, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Warning | 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() // ;-) |
April 17, 2007 Re: advanced function binding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Moritz Warning | 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() // ;-)
|
Copyright © 1999-2021 by the D Language Foundation