Before it's too late: delegate calling convention

Burton Radons burton-radons at smocky.com
Wed Nov 1 13:42:59 PST 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.



More information about the Digitalmars-d mailing list