Before it's too late: delegate calling convention

Gregor Richards Richards at codu.org
Wed Nov 1 14:47:49 PST 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



More information about the Digitalmars-d mailing list