Jump to page: 1 2
Thread overview
Before it's too late: delegate calling convention
Nov 01, 2006
Lionello Lunesu
Nov 01, 2006
Gregor Richards
Nov 01, 2006
Gregor Richards
Nov 01, 2006
Gregor Richards
Nov 02, 2006
Lionello Lunesu
Nov 02, 2006
Gregor Richards
Nov 02, 2006
Lionello Lunesu
Nov 01, 2006
Burton Radons
Nov 01, 2006
Gregor Richards
Nov 01, 2006
Burton Radons
Nov 01, 2006
Gregor Richards
November 01, 2006
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
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
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
"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
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
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
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
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
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
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