View mode: basic / threaded / horizontal-split · Log in · Help
November 01, 2006
Before it's too late: delegate calling convention
Just thought I'd post this before it's too late (some will argue it 
already is too late, but I don't think so):

Please make the calling convention for delegates compatible with the one 
for functions. I want to be able to write my API using only delegates, 
and allow users to pass also globals (function pointers) for callbacks.

The two workarounds currently are

* wrapping the function in a dummy delegate literal (I don't like having 
to do this; it's not about the work, but it looks clumsy)

* provide an API using delegates and one using function pointers (much 
like std.thread.Thread's constructor; needs extra code, for what?)

If I'd be asked to submit ONE issue for "to do before 1.0", this would 
be it.

L.
November 01, 2006
Re: Before it's too late: delegate calling convention
Lionello Lunesu wrote:
> Just thought I'd post this before it's too late (some will argue it 
> already is too late, but I don't think so):
> 
> Please make the calling convention for delegates compatible with the one 
> for functions. I want to be able to write my API using only delegates, 
> and allow users to pass also globals (function pointers) for callbacks.
> 
> The two workarounds currently are
> 
> * wrapping the function in a dummy delegate literal (I don't like having 
> to do this; it's not about the work, but it looks clumsy)
> 
> * provide an API using delegates and one using function pointers (much 
> like std.thread.Thread's constructor; needs extra code, for what?)
> 
> If I'd be asked to submit ONE issue for "to do before 1.0", this would 
> be it.
> 
> L.

One major problem: This would make function pointers incompatible with C 
function pointers, making interfacing with a huge chunk of software 
impossible.

One solution nobody will like (hell, I don't even like it): Have a 
special value for the scope part of the delegate that basically implies 
"this is not really a delegate", and will cause the call to the delegate 
to not pass it as the first argument. This would incur an if for 
delegate calls, but would make function pointers castable to delegates.

 - Gregor Richards
November 01, 2006
Re: Before it's too late: delegate calling convention
Gregor Richards wrote:
> Lionello Lunesu wrote:
> 
>> Just thought I'd post this before it's too late (some will argue it 
>> already is too late, but I don't think so):
>>
>> Please make the calling convention for delegates compatible with the 
>> one for functions. I want to be able to write my API using only 
>> delegates, and allow users to pass also globals (function pointers) 
>> for callbacks.
>>
>> The two workarounds currently are
>>
>> * wrapping the function in a dummy delegate literal (I don't like 
>> having to do this; it's not about the work, but it looks clumsy)
>>
>> * provide an API using delegates and one using function pointers (much 
>> like std.thread.Thread's constructor; needs extra code, for what?)
>>
>> If I'd be asked to submit ONE issue for "to do before 1.0", this would 
>> be it.
>>
>> L.
> 
> 
> One major problem: This would make function pointers incompatible with C 
> function pointers, making interfacing with a huge chunk of software 
> impossible.
> 
> One solution nobody will like (hell, I don't even like it): Have a 
> special value for the scope part of the delegate that basically implies 
> "this is not really a delegate", and will cause the call to the delegate 
> to not pass it as the first argument. This would incur an if for 
> delegate calls, but would make function pointers castable to delegates.
> 
>  - Gregor Richards

And a solution that's less nasty: Allow casting of function pointers to 
delegate pointers by automatic thunktion generation.

 - Gregor Richards
November 01, 2006
Re: Before it's too late: delegate calling convention
"Gregor Richards" <Richards@codu.org> wrote in message 
news:4548CD75.80803@codu.org...

> And a solution that's less nasty: Allow casting of function pointers to 
> delegate pointers by automatic thunktion generation.

I like that a lot better.  But if you're wrapping a function pointer in a 
delegate reference, is a thunk really needed?  I mean, the function won't be 
accessing the context pointer, so why not just set the delegate's context 
pointer to null?

i.e.

int foo()
{

}

int delegate() bar = cast(int delegate())&foo;
// bar's function address is &foo, and bar.ptr == null
November 01, 2006
Re: Before it's too late: delegate calling convention
Gregor Richards wrote:
> And a solution that's less nasty: Allow casting of function pointers to 
> delegate pointers by automatic thunktion generation.

And vice versa, they'd actually be very simple with D. As a matter of 
fact...

	void thunk_delegate_to_function_args () { asm { naked; mov ECX, EAX; 
mov EAX, [ESP+4]; jmp ECX; } }
	void thunk_delegate_to_function_noargs () { asm { naked; jmp EAX; } }
	
	template ThunkBase (From, To, bool HasArgs)
	{
		To ThunkBase (From from)
		{
			size_t thunk_length;
			ubyte [] data;
			size_t pos;
			To to;
			
			void add (ubyte [] stuff...) { data [pos .. pos + stuff.length] = 
stuff; pos += stuff.length; }
			
			static if (from.sizeof == 4)
			{
				union Delegate { struct { void *data, func; } To result; }
				Delegate output;
				
				output.data = from;
				static if (HasArgs)
					output.func = &thunk_delegate_to_function_args;
				else
					output.func = &thunk_delegate_to_function_noargs;
				return output.result;
			}
			else
			{
				static assert (to.sizeof == 4);
				
				union Delegate { struct { void *self, func; } From all; }
				Delegate fromd;
				
				fromd.all = from;
				
				thunk_length = 10 + 13;
				data = new ubyte [thunk_length];
				
				add (0x8B, 0x0C, 0x24, 0x83, 0xEC, 0x04, 0x89, 0x0C, 0x24, 0x89, 
0x44, 0x24, 0x04); // mov ECX, [ESP+0]; sub ESP, 4; mov [ESP+0], ECX; 
mov [ESP+4], EAX;
				add (0xB8); add ((cast (ubyte *) &fromd.self) [0 .. 4]); // mov EAX, 
from.this;
				
				int offset = cast (int) (cast (void *) fromd.func - (cast (void *) 
data + pos + 5));
				add (0xE9); add ((cast (ubyte *) &offset) [0 .. 4]);
				
				return cast (To) data.ptr;
			}
		}
	}
	
	/// Convert a delegate into a function or vice versa.
	struct Thunk (ReturnType, A = void, B = void, C = void, D = void, E = 
void, F = void, G = void, H = void, I = void, J = void, K = void, L = 
void, M = void, N = void, O = void, P = void, Q = void, R = void, S = 
void, T = void, U = void, V = void, W = void, X = void, Y = void, Z = void)
	{
		static if (is (A == void)) { alias ReturnType delegate () TD; alias 
ReturnType function () TF; }
		else static if (is (B == void)) { alias ReturnType delegate (A) TD; 
alias ReturnType function (A) TF; }
		else static if (is (C == void)) { alias ReturnType delegate (A, B) TD; 
alias ReturnType function (A, B) TF; }
		else static if (is (D == void)) { alias ReturnType delegate (A, B, C) 
TD; alias ReturnType function (A, B, C) TF; }
		else static if (is (E == void)) { alias ReturnType delegate (A, B, C, 
D) TD; alias ReturnType function (A, B, C, D) TF; }
		else static if (is (F == void)) { alias ReturnType delegate (A, B, C, 
D, E) TD; alias ReturnType function (A, B, C, D, E) TF; }
		else static if (is (G == void)) { alias ReturnType delegate (A, B, C, 
D, E, F) TD; alias ReturnType function (A, B, C, D, E, F) TF; }
		else static if (is (H == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G) TD; alias ReturnType function (A, B, C, D, E, F, G) TF; }
		else static if (is (I == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H) TD; alias ReturnType function (A, B, C, D, E, F, G, H) TF; }
		else static if (is (J == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I) TD; alias ReturnType function (A, B, C, D, E, F, G, H, 
I) TF; }
		else static if (is (K == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J) TD; alias ReturnType function (A, B, C, D, E, F, G, 
H, I, J) TF; }
		else static if (is (L == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K) TD; alias ReturnType function (A, B, C, D, E, F, 
G, H, I, J, K) TF; }
		else static if (is (M == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L) TD; alias ReturnType function (A, B, C, D, E, 
F, G, H, I, J, K, L) TF; }
		else static if (is (N == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M) TD; alias ReturnType function (A, B, C, D, 
E, F, G, H, I, J, K, L, M) TF; }
		else static if (is (O == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N) TD; alias ReturnType function (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N) TF; }
		else static if (is (P == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O) TD; alias ReturnType function (A, B, 
C, D, E, F, G, H, I, J, K, L, M, N, O) TF; }
		else static if (is (Q == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P) TD; alias ReturnType function (A, 
B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) TF; }
		else static if (is (R == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q) TD; alias ReturnType function 
(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) TF; }
		else static if (is (S == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) TD; alias ReturnType 
function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) TF; }
		else static if (is (T == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) TD; alias ReturnType 
function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) TF; }
		else static if (is (U == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) TD; alias ReturnType 
function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) TF; }
		else static if (is (V == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) TD; alias 
ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
R, S, T, U) TF; }
		else static if (is (W == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) TD; alias 
ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
R, S, T, U, V) TF; }
		else static if (is (X == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W) TD; alias 
ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
R, S, T, U, V, W) TF; }
		else static if (is (Y == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X) TD; alias 
ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
R, S, T, U, V, W, X) TF; }
		else static if (is (Z == void)) { alias ReturnType delegate (A, B, C, 
D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y) TD; 
alias ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, 
P, Q, R, S, T, U, V, W, X, Y) TF; }
		else { alias ReturnType delegate (A, B, C, D, E, F, G, H, I, J, K, L, 
M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z) TD; alias ReturnType function 
(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, 
Y, Z) TF; }
	
		
		static if (!is (A == void) && A.sizeof <= 4 && !is (A == float))
			const bool hasargs = true;
		else
			const bool hasargs = false;
		
		static TD opCall (TF from)
		{
			return ThunkBase! (TF, TD, hasargs) (from);
		}
		
		static TF opCall (TD from)
		{
			return ThunkBase! (TD, TF, hasargs) (from);
		}
	}

Use like:

   int zambo (float a, float b) { writef ("Hello %s, %s!\n", a, b); }
   alias Thunk! (int, float, float) thunker;
   auto rambo = thunker (zambo);
   rambo (16, 32);

Note that casting a function to a delegate doesn't even require an 
allocation, it's just a matter of choosing the right wrapper.

Not tested thoroughly since I don't care, it'll likely fail like crazy 
if you do bad things to it.
November 01, 2006
Re: Before it's too late: delegate calling convention
Jarrett Billingsley wrote:
> "Gregor Richards" <Richards@codu.org> wrote in message 
> news:4548CD75.80803@codu.org...
> 
> 
>>And a solution that's less nasty: Allow casting of function pointers to 
>>delegate pointers by automatic thunktion generation.
> 
> 
> I like that a lot better.  But if you're wrapping a function pointer in a 
> delegate reference, is a thunk really needed?  I mean, the function won't be 
> accessing the context pointer, so why not just set the delegate's context 
> pointer to null?
> 
> i.e.
> 
> int foo()
> {
> 
> }
> 
> int delegate() bar = cast(int delegate())&foo;
> // bar's function address is &foo, and bar.ptr == null 
> 
> 

The context pointer isn't magic, it's passed as an argument to the 
function. So if you directly casted, you'd end up with an extra argument 
passed to the function, any argument processing would be off-by-one.

 - Gregor Richards
November 01, 2006
Re: Before it's too late: delegate calling convention
Burton Radons wrote:
> Gregor Richards wrote:
> 
>> And a solution that's less nasty: Allow casting of function pointers 
>> to delegate pointers by automatic thunktion generation.
> 
> 
> And vice versa, they'd actually be very simple with D. As a matter of 
> fact...
> 
>     void thunk_delegate_to_function_args () { asm { naked; mov ECX, EAX; 
> mov EAX, [ESP+4]; jmp ECX; } }
>     void thunk_delegate_to_function_noargs () { asm { naked; jmp EAX; } }
>     
>     template ThunkBase (From, To, bool HasArgs)
>     {
>         To ThunkBase (From from)
>         {
>             size_t thunk_length;
>             ubyte [] data;
>             size_t pos;
>             To to;
>            
>             void add (ubyte [] stuff...) { data [pos .. pos + 
> stuff.length] = stuff; pos += stuff.length; }
>            
>             static if (from.sizeof == 4)
>             {
>                 union Delegate { struct { void *data, func; } To result; }
>                 Delegate output;
>                
>                 output.data = from;
>                 static if (HasArgs)
>                     output.func = &thunk_delegate_to_function_args;
>                 else
>                     output.func = &thunk_delegate_to_function_noargs;
>                 return output.result;
>             }
>             else
>             {
>                 static assert (to.sizeof == 4);
>                
>                 union Delegate { struct { void *self, func; } From all; }
>                 Delegate fromd;
>                
>                 fromd.all = from;
>                
>                 thunk_length = 10 + 13;
>                 data = new ubyte [thunk_length];
>                
>                 add (0x8B, 0x0C, 0x24, 0x83, 0xEC, 0x04, 0x89, 0x0C, 
> 0x24, 0x89, 0x44, 0x24, 0x04); // mov ECX, [ESP+0]; sub ESP, 4; mov 
> [ESP+0], ECX; mov [ESP+4], EAX;
>                 add (0xB8); add ((cast (ubyte *) &fromd.self) [0 .. 4]); 
> // mov EAX, from.this;
>                
>                 int offset = cast (int) (cast (void *) fromd.func - 
> (cast (void *) data + pos + 5));
>                 add (0xE9); add ((cast (ubyte *) &offset) [0 .. 4]);
>                
>                 return cast (To) data.ptr;
>             }
>         }
>     }
>     
>     /// Convert a delegate into a function or vice versa.
>     struct Thunk (ReturnType, A = void, B = void, C = void, D = void, E 
> = void, F = void, G = void, H = void, I = void, J = void, K = void, L = 
> void, M = void, N = void, O = void, P = void, Q = void, R = void, S = 
> void, T = void, U = void, V = void, W = void, X = void, Y = void, Z = void)
>     {
>         static if (is (A == void)) { alias ReturnType delegate () TD; 
> alias ReturnType function () TF; }
>         else static if (is (B == void)) { alias ReturnType delegate (A) 
> TD; alias ReturnType function (A) TF; }
>         else static if (is (C == void)) { alias ReturnType delegate (A, 
> B) TD; alias ReturnType function (A, B) TF; }
>         else static if (is (D == void)) { alias ReturnType delegate (A, 
> B, C) TD; alias ReturnType function (A, B, C) TF; }
>         else static if (is (E == void)) { alias ReturnType delegate (A, 
> B, C, D) TD; alias ReturnType function (A, B, C, D) TF; }
>         else static if (is (F == void)) { alias ReturnType delegate (A, 
> B, C, D, E) TD; alias ReturnType function (A, B, C, D, E) TF; }
>         else static if (is (G == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F) TD; alias ReturnType function (A, B, C, D, E, F) TF; }
>         else static if (is (H == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G) TD; alias ReturnType function (A, B, C, D, E, F, G) TF; }
>         else static if (is (I == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H) TD; alias ReturnType function (A, B, C, D, E, F, G, 
> H) TF; }
>         else static if (is (J == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I) TD; alias ReturnType function (A, B, C, D, E, F, 
> G, H, I) TF; }
>         else static if (is (K == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J) TD; alias ReturnType function (A, B, C, D, E, 
> F, G, H, I, J) TF; }
>         else static if (is (L == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K) TD; alias ReturnType function (A, B, C, D, 
> E, F, G, H, I, J, K) TF; }
>         else static if (is (M == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L) TD; alias ReturnType function (A, B, C, 
> D, E, F, G, H, I, J, K, L) TF; }
>         else static if (is (N == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M) TD; alias ReturnType function (A, B, 
> C, D, E, F, G, H, I, J, K, L, M) TF; }
>         else static if (is (O == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N) TD; alias ReturnType function (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N) TF; }
>         else static if (is (P == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O) TD; alias ReturnType function 
> (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) TF; }
>         else static if (is (Q == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) TD; alias ReturnType 
> function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) TF; }
>         else static if (is (R == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) TD; alias ReturnType 
> function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) TF; }
>         else static if (is (S == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) TD; alias ReturnType 
> function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) TF; }
>         else static if (is (T == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) TD; alias 
> ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
> R, S) TF; }
>         else static if (is (U == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) TD; alias 
> ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
> R, S, T) TF; }
>         else static if (is (V == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) TD; alias 
> ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
> R, S, T, U) TF; }
>         else static if (is (W == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) TD; alias 
> ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, 
> R, S, T, U, V) TF; }
>         else static if (is (X == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W) TD; 
> alias ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, 
> P, Q, R, S, T, U, V, W) TF; }
>         else static if (is (Y == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X) TD; 
> alias ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, 
> P, Q, R, S, T, U, V, W, X) TF; }
>         else static if (is (Z == void)) { alias ReturnType delegate (A, 
> B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y) 
> TD; alias ReturnType function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, 
> O, P, Q, R, S, T, U, V, W, X, Y) TF; }
>         else { alias ReturnType delegate (A, B, C, D, E, F, G, H, I, J, 
> K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z) TD; alias ReturnType 
> function (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, 
> V, W, X, Y, Z) TF; }
>     
>        
>         static if (!is (A == void) && A.sizeof <= 4 && !is (A == float))
>             const bool hasargs = true;
>         else
>             const bool hasargs = false;
>        
>         static TD opCall (TF from)
>         {
>             return ThunkBase! (TF, TD, hasargs) (from);
>         }
>        
>         static TF opCall (TD from)
>         {
>             return ThunkBase! (TD, TF, hasargs) (from);
>         }
>     }
> 
> Use like:
> 
>    int zambo (float a, float b) { writef ("Hello %s, %s!\n", a, b); }
>    alias Thunk! (int, float, float) thunker;
>    auto rambo = thunker (zambo);
>    rambo (16, 32);
> 
> Note that casting a function to a delegate doesn't even require an 
> allocation, it's just a matter of choosing the right wrapper.
> 
> Not tested thoroughly since I don't care, it'll likely fail like crazy 
> if you do bad things to it.

It's also DMD-with-D-calling-convention-specific X_X

(eg: won't work with GDC, won't work with C functions)

 - Gregor Richards
November 01, 2006
Re: Before it's too late: delegate calling convention
Gregor Richards wrote:
> It's also DMD-with-D-calling-convention-specific X_X
> 
> (eg: won't work with GDC, won't work with C functions)

It could easily be done, just have the thunks bounce into a function the 
Thunk template builds that reorder the arguments (this function can 
gratefully be written in D), and add a post-call patch to fix the stack.

I'm not trying to argue that this is the way it should be, obviously the 
compiler should be handling this magic. But while I've never heard 
Walter say anything about thunking, I can predict with great accuracy 
that he'll be against it because it's a hidden allocation [of great 
utility]. It seems like it would be better to have SOMETHING to do the 
task than to keep crashing against the barricade and getting more and 
more frustrated.

Anyway, thunking D to C would generally be pretty hazardous because as 
soon as you pass it to the C library it'll be collectable, and since the 
allocation IS hidden it would be easy to miss. Manually thunking C 
function pointers isn't difficult with any correctly-written C library 
and doesn't happen often enough in my experience (once per wrapper, to 
be exact) to bother with.

What happens more often is the need to cast from function pointers to 
delegates (or SOME common function pointer), constrained to the D 
calling convention. Since that can be done without allocations, I think 
that and only that should be made part of the language. The rest can 
follow with time, or not.
November 01, 2006
Re: Before it's too late: delegate calling convention
Burton Radons wrote:
> Gregor Richards wrote:
> 
>> It's also DMD-with-D-calling-convention-specific X_X
>>
>> (eg: won't work with GDC, won't work with C functions)
> 
> 
> It could easily be done, just have the thunks bounce into a function the 
> Thunk template builds that reorder the arguments (this function can 
> gratefully be written in D), and add a post-call patch to fix the stack.
> 
> I'm not trying to argue that this is the way it should be, obviously the 
> compiler should be handling this magic. But while I've never heard 
> Walter say anything about thunking, I can predict with great accuracy 
> that he'll be against it because it's a hidden allocation [of great 
> utility]. It seems like it would be better to have SOMETHING to do the 
> task than to keep crashing against the barricade and getting more and 
> more frustrated.
> 
> Anyway, thunking D to C would generally be pretty hazardous because as 
> soon as you pass it to the C library it'll be collectable, and since the 
> allocation IS hidden it would be easy to miss. Manually thunking C 
> function pointers isn't difficult with any correctly-written C library 
> and doesn't happen often enough in my experience (once per wrapper, to 
> be exact) to bother with.
> 
> What happens more often is the need to cast from function pointers to 
> delegates (or SOME common function pointer), constrained to the D 
> calling convention. Since that can be done without allocations, I think 
> that and only that should be made part of the language. The rest can 
> follow with time, or not.

If I recall properly, DMC uses thunktions for C++'s lame pseudodelegates :)

 - Gregor Richards
November 02, 2006
Re: Before it's too late: delegate calling convention
Gregor Richards wrote:
> Jarrett Billingsley wrote:
>> "Gregor Richards" <Richards@codu.org> wrote in message 
>> news:4548CD75.80803@codu.org...
>>
>>
>>> And a solution that's less nasty: Allow casting of function pointers 
>>> to delegate pointers by automatic thunktion generation.
>>
>>
>> I like that a lot better.  But if you're wrapping a function pointer 
>> in a delegate reference, is a thunk really needed?  I mean, the 
>> function won't be accessing the context pointer, so why not just set 
>> the delegate's context pointer to null?
>>
>> i.e.
>>
>> int foo()
>> {
>>
>> }
>>
>> int delegate() bar = cast(int delegate())&foo;
>> // bar's function address is &foo, and bar.ptr == null
>>
> 
> The context pointer isn't magic, it's passed as an argument to the 
> function. So if you directly casted, you'd end up with an extra argument 
> passed to the function, any argument processing would be off-by-one.
> 
>  - Gregor Richards

Actually, it's passed in a register, so it could be made to work.

L.
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home