Thread overview
advanced function binding
Mar 28, 2007
Moritz Warning
Mar 28, 2007
Kirk McDonald
Mar 28, 2007
Moritz Warning
Mar 28, 2007
Kirk McDonald
Mar 29, 2007
Moritz Warning
Mar 29, 2007
Kirk McDonald
Mar 29, 2007
Moritz Warning
Mar 31, 2007
Moritz Warning
Apr 17, 2007
Jason House
Mar 30, 2007
Jason House
March 28, 2007
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
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
> 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
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
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
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
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
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
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
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()  // ;-)