www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - RPC and Dynamic function call

reply "Denis Koroskin" <2korden gmail.com> writes:
------------m2pXpz6uLQtkfFRnyWpADe
Content-Type: text/plain; charset=koi8-r; format=flowed; delsp=yes
Content-Transfer-Encoding: 7bit

I am working on a prototype of PRC library and it involves a wide range of  
techniques that I also implement. I'd like to share my code,  
implementation details and issues I come across. I will split my report  
into a few posts. This one is mostly an introduction.

RPC is a concept that allows to execute some code in a remote application.  
To make it work, you must know function name, a set of arguments it  
accepts, type it returns etc.

This is usually is not a problem if local and remote applications are same  
or based on shared code.

The two applications may be ran in different environments (different  
Operation Systems, different memory modes, x86/x64 etc).

In order to make a call a few components are required:
Function (one that we want to call), context (remote object reference; not  
needed for static member functions and global/free functions), input/ouput  
arguments.

A RPC requested is created locally. It contains everything in a list  
above. Input arguments might be reference type, in which case they might  
have references to other objects which in turn have references to other  
objects etc. In general, everything accessible through a set of input  
arguments might be accessed on remote side. This is why the whole object  
hierarchy accessible through input arguments need to be saved, sent over  
network and fully restored on remote side.

This is called serialization. The fastest and most compact form of  
serialization is binary serialization. I'll talk about serialization in  
one of the next posts.

Next thing we need to do is to somehow encode the function we are gonna  
call in a way that remote application will understand and get a proper  
function pointer out of this information. This has something to do with  
reflection (although pretty simple mechanism is required).

Okay, now we received a RPC request that contains function pointer and a  
set of input arguments. We also have some knowledge on ouput arguments.  
All we need to do is to call that function and return back result.

At this point, it's worth to note that all we have at remote side is some  
void[] array. No types. Once we step out of type system, there is no way  
back. It means that you can't really call a function in a type-safe manner  
anymore (well, one could create a set of trampolines for each of set of  
types involved in a call, but I don't think it's reasonable or even  
possible; I'll look into it, too, though). That's why you need to make a  
dynamic call: iterate over some kind of input arguments' type information,  
push values to stack, call function via funcptr, store the result etc.

I was the last piece to make RPC work, that's what I was implementing  
today and that's what I will share in this post. The resulting code is  
quite small and simple.

Note that all the asm it uses is just 3 constructs: naked, ret and call!  
Everything else is implemented with a help of compiler (i.e. pushing  
arguments, grabbing result, stack alignment etc). My code is most probably  
not very portable (I didn't test it on anything other that Windows), it  
doesn't account x64 specifics etc, but it shouldn't be hard to fix.

When could it be useful? A few applications include RPC, reflection  
(possibly), binding to other languages (including scripting ones), code  
injection/hijacking, run-time code generation etc.

Any suggestions, critics, comments are highly appreciated.

Full listing below (tested with DMD2.036, Windows XP / Windows 7):

module DynamicCall;

import std.traits;
import std.stdio;

enum ParamType
{
	// No return value
	Void,

	// ST0
	Float,
	Double,
	Real,

	// EAX
	Byte,
	Word,
	DWord,
	Pointer,
	// AArray, 		// isn't tested (merge with Pointer?)

	//EDX,EAX
	QWord,
	DArray,		 // isn't tested

	// Hidden pointer
	Hidden_Pointer,	// for returning large (>8 bytes) structs, not tested yet
}

struct Param
{
	ParamType type;	// type of value
	void* ptr;		// pointer to value
}

// This struct describes everything needed to make a call
struct Call
{
	Param[] input;	// set of input arguments
	Param output;	// result info
	void* funcptr;	// function pointer
}

// makes a call, stores result in call.ouput
void makeDynamicCall(Call* call)
{
	switch (call.output.type) {
		case ParamType.Void:
			_makeCall!(void)(call);
			break;

		case ParamType.Pointer:
			makeCall!(void*)(call);
			break;

		case ParamType.Byte:
			makeCall!(byte)(call);
			break;
			
		case ParamType.Word:
			makeCall!(short)(call);
			break;
			
		case ParamType.DWord:
			makeCall!(int)(call);
			break;
			
		case ParamType.QWord:
			makeCall!(long)(call);
			break;

		case ParamType.Float:
			makeCall!(float)(call);
			break;

		case ParamType.Double:
			makeCall!(double)(call);
			break;
			
		case ParamType.Real:
			makeCall!(real)(call);
			break;
	}
}

// helper function to save some typing
void makeCall(T)(Call* call)
{
	*cast(T*)call.output.ptr = _makeCall!(T)(call);
}

T _makeCall(T)(Call* call)
{
	void* funcptr = call.funcptr;
	void* argptr;

	int numArgs = call.input.length;
	
	if (numArgs != 0) {	// this check is needed because last parameter is  
passed in EAX (if possible)
		Param* param = call.input.ptr;
		
		// iterate over first numArgs-1 arguments
		for ( ; --numArgs; ++param) {
			/*
			// the following doesn't work for some reason (compiles but lead to  
wrong result in run-time)
			// would be so much more elegant!
			push!(arg)(param);
			/*/
			argptr = param.ptr;
			switch (param.type) {
				case ParamType.Byte:	// push byte
					arg(*cast(byte*)argptr);
					break;
					
				case ParamType.Word:	// push word
					arg(*cast(short*)argptr);
					break;

				case ParamType.Pointer:
				case ParamType.DWord:	// push dword
					arg(*cast(int*)argptr);
					break;

				case ParamType.QWord:	// push qword
					arg(*cast(long*)argptr);
					break;

				case ParamType.Float:	// push float
					arg(*cast(float*)argptr);
					break;

				case ParamType.Double:	// push double
					arg(*cast(double*)argptr);
					break;
					
				case ParamType.Real:	// push real
					arg(*cast(real*)argptr);
					break;
			}
			//*/
		}

		// same as above but passes in EAX if possible

		/*
		push!(lastArg)(param);
		/*/
		argptr = param.ptr;
		switch (param.type) {
			case ParamType.Byte:
				lastArg(*cast(byte*)argptr);
				break;
				
			case ParamType.Word:
				lastArg(*cast(short*)argptr);
				break;
				
			case ParamType.Pointer:
			case ParamType.DWord:
				lastArg(*cast(int*)argptr);
				break;

			case ParamType.QWord:
				lastArg(*cast(long*)argptr);
				break;

			case ParamType.Float:
				lastArg(*cast(float*)argptr);
				break;

			case ParamType.Double:
				lastArg(*cast(double*)argptr);
				
			case ParamType.Real:
				lastArg(*cast(real*)argptr);
		}
		//*/
	}

	asm {
		// call it!
		call funcptr;
	}
}

// A helper function that pushes an argument to stack in a type-safe manner
// extern (System) is used so that argument isn't passed via EAX
// does it work the same way in Linux? Or Linux uses __cdecl?
// There must be other way to pass all the arguments on stack, but this  
one works well so far
// Beautiful, isn't it?
extern (System) void arg(T)(T arg)
{
	asm {
		naked;
		ret;
	}
}

// A helper function that pushes an argument to stack in a type-safe manner
// Allowed to pass argumet via EAX (that's why it's extern (D))
void lastArg(T)(T arg)
{
	asm {
		naked;
		ret;
	}
}

// Compare it to my older implementation:
/+
T _makeCall(T)(Call* call)
{
	void* funcptr = call.funcptr;
	void* argptr;
	int i = call.input.length;
	
	int eax = -1;

	foreach (ref param; call.input) {
		--i;
		argptr = param.ptr;

		switch (param.type) {
			case ParamType.Byte:
				// passing word
				asm {
					mov EDX, argptr;
					mov AL, byte ptr[EDX];
				}
				if (i != 0) {
					asm {
						push EAX;
					}
				} else {
					asm {
						mov eax, EAX;
					}
				}
				break;
				
			case ParamType.Word:
				// passing word
				asm {
					mov EDX, argptr;
					mov AX, word ptr[EDX];
				}
				if (i != 0) {
					asm {
						push EAX;
					}
				} else {
					asm {
						mov eax, EAX;
					}
				}
				break;
			
			case ParamType.Pointer:
			case ParamType.DWord:
				// passing word
				asm {
					mov EDX, argptr;
					mov EAX, dword ptr[EDX];
				}
				if (i != 0) {
					asm {
						push EAX;
					}
				} else {
					asm {
						mov eax, EAX;
					}
				}
				break;
				
			case ParamType.QWord:
				// pushing word
				asm {
					mov EDX, argptr;
					mov EAX, dword ptr[EDX+4];
					push EAX;
					mov EAX, dword ptr[EDX];
					push EAX;
				}
				break;

			case ParamType.Float:
				// pushing float
				asm {
					sub ESP, 4;
					mov EAX, dword ptr[argptr];
					fld dword ptr[EAX];
					fstp dword ptr[ESP];
				}
				break;

			case ParamType.Double:
				// pushing double
				asm {
					sub ESP, 8;
					mov EAX, qword ptr[argptr];
					fld qword ptr[EAX];
					fstp qword ptr[ESP];
				}
				break;
		}
	}

	asm {
		mov EAX, eax;
		call funcptr;
	}
}
+/

// I was trying to move out common code to a separate function, but  
failed. It doesn't work for reasons unknown to me
/+
void push(alias fun)(Param* param)
{
	switch (param.type) {
		case ParamType.Byte:
			fun(*cast(byte*)param.ptr);
			break;
			
		case ParamType.Word:
			fun(*cast(short*)param.ptr);
			break;

		case ParamType.Pointer:
		case ParamType.DWord:
			fun(*cast(int*)param.ptr);
			break;

		case ParamType.QWord:
			fun(*cast(long*)param.ptr);
			break;

		case ParamType.Float:
			fun(*cast(float*)param.ptr);
			break;

		case ParamType.Double:
			fun(*cast(double*)param.ptr);
			break;
			
		case ParamType.Real:
			fun(*cast(real*)param.ptr);
			break;
	}
}
+/

// Convenient templates to map from type T to corresponding ParamType enum  
element

template isStructSize(T, int size)
{
	enum isStructSize = is (T == struct) && T.sizeof == size;
}

template ParamTypeFromT(T) if (is (T == byte) || is (T == ubyte) || is (T  
== char) || isStructSize!(T, 1))
{
	alias ParamType.Byte ParamTypeFromT;
}

template ParamTypeFromT(T) if (is (T == short) || is (T == ushort) || is  
(T == wchar) || isStructSize!(T, 2))
{
	alias ParamType.Word ParamTypeFromT;
}

template ParamTypeFromT(T) if (is (T == int) || is (T == uint) || is (T ==  
dchar) || isStructSize!(T, 4))
{
	alias ParamType.DWord ParamTypeFromT;
}

template ParamTypeFromT(T) if (is (T == long) || is (T == ulong) ||  
isStructSize!(T, 8) || is (T == delegate))
{
	alias ParamType.QWord ParamTypeFromT;
}

template ParamTypeFromT(T) if (is (T == float))
{
	alias ParamType.Float ParamTypeFromT;
}

template ParamTypeFromT(T) if (is (T == double))
{
	alias ParamType.Double ParamTypeFromT;
}

template ParamTypeFromT(T) if (is (T == real))
{
	alias ParamType.Real ParamTypeFromT;
}

template ParamTypeFromT(T) if (is (T == void))
{
	alias ParamType.Void ParamTypeFromT;
}

template ParamTypeFromT(T) if (isPointer!(T))
{
	alias ParamType.Pointer ParamTypeFromT;
}

Test case:

import DynamicCall;

import std.stdio;

Param createParam(T)(T value)
{
	//T* ptr = new T;	// BUG! Doesn't work for delegate type
	T* ptr = cast(T*)(new void[T.sizeof]).ptr;
	*ptr = value;

	Param param;
	param.type = ParamTypeFromT!(T);
	param.ptr = cast(void*)ptr;

	return param;
}

struct b1
{
	char c;
}

struct b2
{
	wchar w;
}

struct b4
{
	dchar d;
}

real foo(byte b, short s, int i, long l, float f, double d, real r, b1 c,  
b2 w, b4 d2, int function() func, int delegate() dg, int* ptr)
{
	writeln(b + s + i + l + f + d + r + c.c + w.w + d2.d + func() + dg() +  
*ptr);

	return 13;
}

int func()
{
	return 42;
}

void main()
{
	int dg()
	{
		return 14;
	}

	Call call;
	call.funcptr = &foo;
	
	int* i = new int;
	*i = 128;

	call.input ~= createParam!(byte)(cast(byte)1);	// BUG! cast is mandatory  
to compile
	call.input ~= createParam!(short)(200);
	call.input ~= createParam!(int)(4);
	call.input ~= createParam!(long)(cast(long)8);
	call.input ~= createParam!(float)(16.0f);
	call.input ~= createParam!(double)(32.0);
	call.input ~= createParam!(real)(64.0);
	call.input ~= createParam!(b1)(b1(1));
	call.input ~= createParam!(b2)(b2(2));
	call.input ~= createParam!(b4)(b4(4));
	call.input ~= createParam!(int function())(&func);
	call.input ~= createParam!(int delegate())(&dg);
	call.input ~= createParam!(int*)(i);

	call.output.type = ParamType.Real;
	call.output.ptr = new real;

	makeDynamicCall(&call);
	
	writeln(*cast(real*)call.output.ptr);
}
------------m2pXpz6uLQtkfFRnyWpADe
Content-Disposition: attachment; filename=DynamicCall.d
Content-Type: application/octet-stream; name=DynamicCall.d
Content-Transfer-Encoding: Base64

bW9kdWxlIER5bmFtaWNDYWxsOw0KDQppbXBvcnQgc3RkLnRyYWl0czsNCmltcG9y
dCBzdGQuc3RkaW87DQoNCmVudW0gUGFyYW1UeXBlDQp7DQoJLy8gTm8gcmV0dXJu
IHZhbHVlDQoJVm9pZCwNCg0KCS8vIFNUMA0KCUZsb2F0LA0KCURvdWJsZSwNCglS
ZWFsLA0KDQoJLy8gRUFYDQoJQnl0ZSwNCglXb3JkLA0KCURXb3JkLA0KCVBvaW50
ZXIsDQoJLy8gQUFycmF5LCAJCS8vIGlzbid0IHRlc3RlZCAobWVyZ2Ugd2l0aCBQ
b2ludGVyPykNCg0KCS8vRURYLEVBWA0KCVFXb3JkLA0KCURBcnJheSwJCSAvLyBp
c24ndCB0ZXN0ZWQNCg0KCS8vIEhpZGRlbiBwb2ludGVyDQoJSGlkZGVuX1BvaW50
ZXIsCS8vIGZvciByZXR1cm5pbmcgbGFyZ2UgKD44IGJ5dGVzKSBzdHJ1Y3RzLCBu
b3QgdGVzdGVkIHlldA0KfQ0KDQpzdHJ1Y3QgUGFyYW0NCnsNCglQYXJhbVR5cGUg
dHlwZTsJLy8gdHlwZSBvZiB2YWx1ZQ0KCXZvaWQqIHB0cjsJCS8vIHBvaW50ZXIg
dG8gdmFsdWUNCn0NCg0KLy8gVGhpcyBzdHJ1Y3QgZGVzY3JpYmVzIGV2ZXJ5dGhp
bmcgbmVlZGVkIHRvIG1ha2UgYSBjYWxsDQpzdHJ1Y3QgQ2FsbA0Kew0KCVBhcmFt
W10gaW5wdXQ7CS8vIHNldCBvZiBpbnB1dCBhcmd1bWVudHMNCglQYXJhbSBvdXRw
dXQ7CS8vIHJlc3VsdCBpbmZvDQoJdm9pZCogZnVuY3B0cjsJLy8gZnVuY3Rpb24g
cG9pbnRlcg0KfQ0KDQovLyBtYWtlcyBhIGNhbGwsIHN0b3JlcyByZXN1bHQgaW4g
Y2FsbC5vdXB1dA0Kdm9pZCBtYWtlRHluYW1pY0NhbGwoQ2FsbCogY2FsbCkNCnsN
Cglzd2l0Y2ggKGNhbGwub3V0cHV0LnR5cGUpIHsNCgkJY2FzZSBQYXJhbVR5cGUu
Vm9pZDoNCgkJCV9tYWtlQ2FsbCEodm9pZCkoY2FsbCk7DQoJCQlicmVhazsNCg0K
CQljYXNlIFBhcmFtVHlwZS5Qb2ludGVyOg0KCQkJbWFrZUNhbGwhKHZvaWQqKShj
YWxsKTsNCgkJCWJyZWFrOw0KDQoJCWNhc2UgUGFyYW1UeXBlLkJ5dGU6DQoJCQlt
YWtlQ2FsbCEoYnl0ZSkoY2FsbCk7DQoJCQlicmVhazsNCgkJCQ0KCQljYXNlIFBh
cmFtVHlwZS5Xb3JkOg0KCQkJbWFrZUNhbGwhKHNob3J0KShjYWxsKTsNCgkJCWJy
ZWFrOw0KCQkJDQoJCWNhc2UgUGFyYW1UeXBlLkRXb3JkOg0KCQkJbWFrZUNhbGwh
KGludCkoY2FsbCk7DQoJCQlicmVhazsNCgkJCQ0KCQljYXNlIFBhcmFtVHlwZS5R
V29yZDoNCgkJCW1ha2VDYWxsIShsb25nKShjYWxsKTsNCgkJCWJyZWFrOw0KDQoJ
CWNhc2UgUGFyYW1UeXBlLkZsb2F0Og0KCQkJbWFrZUNhbGwhKGZsb2F0KShjYWxs
KTsNCgkJCWJyZWFrOw0KDQoJCWNhc2UgUGFyYW1UeXBlLkRvdWJsZToNCgkJCW1h
a2VDYWxsIShkb3VibGUpKGNhbGwpOw0KCQkJYnJlYWs7DQoJCQkNCgkJY2FzZSBQ
YXJhbVR5cGUuUmVhbDoNCgkJCW1ha2VDYWxsIShyZWFsKShjYWxsKTsNCgkJCWJy
ZWFrOw0KCX0NCn0NCg0KLy8gaGVscGVyIGZ1bmN0aW9uIHRvIHNhdmUgc29tZSB0
eXBpbmcNCnZvaWQgbWFrZUNhbGwoVCkoQ2FsbCogY2FsbCkNCnsNCgkqY2FzdChU
KiljYWxsLm91dHB1dC5wdHIgPSBfbWFrZUNhbGwhKFQpKGNhbGwpOw0KfQ0KDQpU
IF9tYWtlQ2FsbChUKShDYWxsKiBjYWxsKQ0Kew0KCXZvaWQqIGZ1bmNwdHIgPSBj
YWxsLmZ1bmNwdHI7DQoJdm9pZCogYXJncHRyOw0KDQoJaW50IG51bUFyZ3MgPSBj
YWxsLmlucHV0Lmxlbmd0aDsNCgkNCglpZiAobnVtQXJncyAhPSAwKSB7CS8vIHRo
aXMgY2hlY2sgaXMgbmVlZGVkIGJlY2F1c2UgbGFzdCBwYXJhbWV0ZXIgaXMgcGFz
c2VkIGluIEVBWCAoaWYgcG9zc2libGUpDQoJCVBhcmFtKiBwYXJhbSA9IGNhbGwu
aW5wdXQucHRyOw0KCQkNCgkJLy8gaXRlcmF0ZSBvdmVyIGZpcnN0IG51bUFyZ3Mt
MSBhcmd1bWVudHMNCgkJZm9yICggOyAtLW51bUFyZ3M7ICsrcGFyYW0pIHsNCgkJ
CS8qDQoJCQkvLyB0aGUgZm9sbG93aW5nIGRvZXNuJ3Qgd29yayBmb3Igc29tZSBy
ZWFzb24gKGNvbXBpbGVzIGJ1dCBsZWFkIHRvIHdyb25nIHJlc3VsdCBpbiBydW4t
dGltZSkNCgkJCS8vIHdvdWxkIGJlIHNvIG11Y2ggbW9yZSBlbGVnYW50IQ0KCQkJ
cHVzaCEoYXJnKShwYXJhbSk7DQoJCQkvKi8NCgkJCWFyZ3B0ciA9IHBhcmFtLnB0
cjsNCgkJCXN3aXRjaCAocGFyYW0udHlwZSkgew0KCQkJCWNhc2UgUGFyYW1UeXBl
LkJ5dGU6CS8vIHB1c2ggYnl0ZQ0KCQkJCQlhcmcoKmNhc3QoYnl0ZSopYXJncHRy
KTsNCgkJCQkJYnJlYWs7DQoJCQkJCQ0KCQkJCWNhc2UgUGFyYW1UeXBlLldvcmQ6
CS8vIHB1c2ggd29yZA0KCQkJCQlhcmcoKmNhc3Qoc2hvcnQqKWFyZ3B0cik7DQoJ
CQkJCWJyZWFrOw0KDQoJCQkJY2FzZSBQYXJhbVR5cGUuUG9pbnRlcjoNCgkJCQlj
YXNlIFBhcmFtVHlwZS5EV29yZDoJLy8gcHVzaCBkd29yZA0KCQkJCQlhcmcoKmNh
c3QoaW50KilhcmdwdHIpOw0KCQkJCQlicmVhazsNCg0KCQkJCWNhc2UgUGFyYW1U
eXBlLlFXb3JkOgkvLyBwdXNoIHF3b3JkDQoJCQkJCWFyZygqY2FzdChsb25nKilh
cmdwdHIpOw0KCQkJCQlicmVhazsNCg0KCQkJCWNhc2UgUGFyYW1UeXBlLkZsb2F0
OgkvLyBwdXNoIGZsb2F0DQoJCQkJCWFyZygqY2FzdChmbG9hdCopYXJncHRyKTsN
CgkJCQkJYnJlYWs7DQoNCgkJCQljYXNlIFBhcmFtVHlwZS5Eb3VibGU6CS8vIHB1
c2ggZG91YmxlDQoJCQkJCWFyZygqY2FzdChkb3VibGUqKWFyZ3B0cik7DQoJCQkJ
CWJyZWFrOw0KCQkJCQkNCgkJCQljYXNlIFBhcmFtVHlwZS5SZWFsOgkvLyBwdXNo
IHJlYWwNCgkJCQkJYXJnKCpjYXN0KHJlYWwqKWFyZ3B0cik7DQoJCQkJCWJyZWFr
Ow0KCQkJfQ0KCQkJLy8qLw0KCQl9DQoNCgkJLy8gc2FtZSBhcyBhYm92ZSBidXQg
cGFzc2VzIGluIEVBWCBpZiBwb3NzaWJsZQ0KDQoJCS8qDQoJCXB1c2ghKGxhc3RB
cmcpKHBhcmFtKTsNCgkJLyovDQoJCWFyZ3B0ciA9IHBhcmFtLnB0cjsNCgkJc3dp
dGNoIChwYXJhbS50eXBlKSB7DQoJCQljYXNlIFBhcmFtVHlwZS5CeXRlOg0KCQkJ
CWxhc3RBcmcoKmNhc3QoYnl0ZSopYXJncHRyKTsNCgkJCQlicmVhazsNCgkJCQkN
CgkJCWNhc2UgUGFyYW1UeXBlLldvcmQ6DQoJCQkJbGFzdEFyZygqY2FzdChzaG9y
dCopYXJncHRyKTsNCgkJCQlicmVhazsNCgkJCQkNCgkJCWNhc2UgUGFyYW1UeXBl
LlBvaW50ZXI6DQoJCQljYXNlIFBhcmFtVHlwZS5EV29yZDoNCgkJCQlsYXN0QXJn
KCpjYXN0KGludCopYXJncHRyKTsNCgkJCQlicmVhazsNCg0KCQkJY2FzZSBQYXJh
bVR5cGUuUVdvcmQ6DQoJCQkJbGFzdEFyZygqY2FzdChsb25nKilhcmdwdHIpOw0K
CQkJCWJyZWFrOw0KDQoJCQljYXNlIFBhcmFtVHlwZS5GbG9hdDoNCgkJCQlsYXN0
QXJnKCpjYXN0KGZsb2F0KilhcmdwdHIpOw0KCQkJCWJyZWFrOw0KDQoJCQljYXNl
IFBhcmFtVHlwZS5Eb3VibGU6DQoJCQkJbGFzdEFyZygqY2FzdChkb3VibGUqKWFy
Z3B0cik7DQoJCQkJDQoJCQljYXNlIFBhcmFtVHlwZS5SZWFsOg0KCQkJCWxhc3RB
cmcoKmNhc3QocmVhbCopYXJncHRyKTsNCgkJfQ0KCQkvLyovDQoJfQ0KDQoJYXNt
IHsNCgkJLy8gY2FsbCBpdCENCgkJY2FsbCBmdW5jcHRyOw0KCX0NCn0NCg0KLy8g
QSBoZWxwZXIgZnVuY3Rpb24gdGhhdCBwdXNoZXMgYW4gYXJndW1lbnQgdG8gc3Rh
Y2sgaW4gYSB0eXBlLXNhZmUgbWFubmVyDQovLyBleHRlcm4gKFN5c3RlbSkgaXMg
dXNlZCBzbyB0aGF0IGFyZ3VtZW50IGlzbid0IHBhc3NlZCB2aWEgRUFYDQovLyBk
b2VzIGl0IHdvcmsgdGhlIHNhbWUgd2F5IGluIExpbnV4PyBPciBMaW51eCB1c2Vz
IF9fY2RlY2w/DQovLyBUaGVyZSBtdXN0IGJlIG90aGVyIHdheSB0byBwYXNzIGFs
bCB0aGUgYXJndW1lbnRzIG9uIHN0YWNrLCBidXQgdGhpcyBvbmUgd29ya3Mgd2Vs
bCBzbyBmYXINCi8vIEJlYXV0aWZ1bCwgaXNuJ3QgaXQ/DQpleHRlcm4gKFN5c3Rl
bSkgdm9pZCBhcmcoVCkoVCBhcmcpDQp7DQoJYXNtIHsNCgkJbmFrZWQ7DQoJCXJl
dDsNCgl9DQp9DQoNCi8vIEEgaGVscGVyIGZ1bmN0aW9uIHRoYXQgcHVzaGVzIGFu
IGFyZ3VtZW50IHRvIHN0YWNrIGluIGEgdHlwZS1zYWZlIG1hbm5lcg0KLy8gQWxs
b3dlZCB0byBwYXNzIGFyZ3VtZXQgdmlhIEVBWCAodGhhdCdzIHdoeSBpdCdzIGV4
dGVybiAoRCkpDQp2b2lkIGxhc3RBcmcoVCkoVCBhcmcpDQp7DQoJYXNtIHsNCgkJ
bmFrZWQ7DQoJCXJldDsNCgl9DQp9DQoNCi8vIENvbXBhcmUgaXQgdG8gbXkgb2xk
ZXIgaW1wbGVtZW50YXRpb246DQovKw0KVCBfbWFrZUNhbGwoVCkoQ2FsbCogY2Fs
bCkNCnsNCgl2b2lkKiBmdW5jcHRyID0gY2FsbC5mdW5jcHRyOw0KCXZvaWQqIGFy
Z3B0cjsNCglpbnQgaSA9IGNhbGwuaW5wdXQubGVuZ3RoOw0KCQ0KCWludCBlYXgg
PSAtMTsNCg0KCWZvcmVhY2ggKHJlZiBwYXJhbTsgY2FsbC5pbnB1dCkgew0KCQkt
LWk7DQoJCWFyZ3B0ciA9IHBhcmFtLnB0cjsNCg0KCQlzd2l0Y2ggKHBhcmFtLnR5
cGUpIHsNCgkJCWNhc2UgUGFyYW1UeXBlLkJ5dGU6DQoJCQkJLy8gcGFzc2luZyB3
b3JkDQoJCQkJYXNtIHsNCgkJCQkJbW92IEVEWCwgYXJncHRyOw0KCQkJCQltb3Yg
QUwsIGJ5dGUgcHRyW0VEWF07DQoJCQkJfQ0KCQkJCWlmIChpICE9IDApIHsNCgkJ
CQkJYXNtIHsNCgkJCQkJCXB1c2ggRUFYOw0KCQkJCQl9DQoJCQkJfSBlbHNlIHsN
CgkJCQkJYXNtIHsNCgkJCQkJCW1vdiBlYXgsIEVBWDsNCgkJCQkJfQ0KCQkJCX0N
CgkJCQlicmVhazsNCgkJCQkNCgkJCWNhc2UgUGFyYW1UeXBlLldvcmQ6DQoJCQkJ
Ly8gcGFzc2luZyB3b3JkDQoJCQkJYXNtIHsNCgkJCQkJbW92IEVEWCwgYXJncHRy
Ow0KCQkJCQltb3YgQVgsIHdvcmQgcHRyW0VEWF07DQoJCQkJfQ0KCQkJCWlmIChp
ICE9IDApIHsNCgkJCQkJYXNtIHsNCgkJCQkJCXB1c2ggRUFYOw0KCQkJCQl9DQoJ
CQkJfSBlbHNlIHsNCgkJCQkJYXNtIHsNCgkJCQkJCW1vdiBlYXgsIEVBWDsNCgkJ
CQkJfQ0KCQkJCX0NCgkJCQlicmVhazsNCgkJCQ0KCQkJY2FzZSBQYXJhbVR5cGUu
UG9pbnRlcjoNCgkJCWNhc2UgUGFyYW1UeXBlLkRXb3JkOg0KCQkJCS8vIHBhc3Np
bmcgd29yZA0KCQkJCWFzbSB7DQoJCQkJCW1vdiBFRFgsIGFyZ3B0cjsNCgkJCQkJ
bW92IEVBWCwgZHdvcmQgcHRyW0VEWF07DQoJCQkJfQ0KCQkJCWlmIChpICE9IDAp
IHsNCgkJCQkJYXNtIHsNCgkJCQkJCXB1c2ggRUFYOw0KCQkJCQl9DQoJCQkJfSBl
bHNlIHsNCgkJCQkJYXNtIHsNCgkJCQkJCW1vdiBlYXgsIEVBWDsNCgkJCQkJfQ0K
CQkJCX0NCgkJCQlicmVhazsNCgkJCQkNCgkJCWNhc2UgUGFyYW1UeXBlLlFXb3Jk
Og0KCQkJCS8vIHB1c2hpbmcgd29yZA0KCQkJCWFzbSB7DQoJCQkJCW1vdiBFRFgs
IGFyZ3B0cjsNCgkJCQkJbW92IEVBWCwgZHdvcmQgcHRyW0VEWCs0XTsNCgkJCQkJ
cHVzaCBFQVg7DQoJCQkJCW1vdiBFQVgsIGR3b3JkIHB0cltFRFhdOw0KCQkJCQlw
dXNoIEVBWDsNCgkJCQl9DQoJCQkJYnJlYWs7DQoNCgkJCWNhc2UgUGFyYW1UeXBl
LkZsb2F0Og0KCQkJCS8vIHB1c2hpbmcgZmxvYXQNCgkJCQlhc20gew0KCQkJCQlz
dWIgRVNQLCA0Ow0KCQkJCQltb3YgRUFYLCBkd29yZCBwdHJbYXJncHRyXTsNCgkJ
CQkJZmxkIGR3b3JkIHB0cltFQVhdOw0KCQkJCQlmc3RwIGR3b3JkIHB0cltFU1Bd
Ow0KCQkJCX0NCgkJCQlicmVhazsNCg0KCQkJY2FzZSBQYXJhbVR5cGUuRG91Ymxl
Og0KCQkJCS8vIHB1c2hpbmcgZG91YmxlDQoJCQkJYXNtIHsNCgkJCQkJc3ViIEVT
UCwgODsNCgkJCQkJbW92IEVBWCwgcXdvcmQgcHRyW2FyZ3B0cl07DQoJCQkJCWZs
ZCBxd29yZCBwdHJbRUFYXTsNCgkJCQkJZnN0cCBxd29yZCBwdHJbRVNQXTsNCgkJ
CQl9DQoJCQkJYnJlYWs7DQoJCX0NCgl9DQoNCglhc20gew0KCQltb3YgRUFYLCBl
YXg7DQoJCWNhbGwgZnVuY3B0cjsNCgl9DQp9DQorLw0KDQovLyBJIHdhcyB0cnlp
bmcgdG8gbW92ZSBvdXQgY29tbW9uIGNvZGUgdG8gYSBzZXBhcmF0ZSBmdW5jdGlv
biwgYnV0IGZhaWxlZC4gSXQgZG9lc24ndCB3b3JrIGZvciByZWFzb25zIHVua25v
d24gdG8gbWUNCi8rDQp2b2lkIHB1c2goYWxpYXMgZnVuKShQYXJhbSogcGFyYW0p
DQp7DQoJc3dpdGNoIChwYXJhbS50eXBlKSB7DQoJCWNhc2UgUGFyYW1UeXBlLkJ5
dGU6DQoJCQlmdW4oKmNhc3QoYnl0ZSopcGFyYW0ucHRyKTsNCgkJCWJyZWFrOw0K
CQkJDQoJCWNhc2UgUGFyYW1UeXBlLldvcmQ6DQoJCQlmdW4oKmNhc3Qoc2hvcnQq
KXBhcmFtLnB0cik7DQoJCQlicmVhazsNCg0KCQljYXNlIFBhcmFtVHlwZS5Qb2lu
dGVyOg0KCQljYXNlIFBhcmFtVHlwZS5EV29yZDoNCgkJCWZ1bigqY2FzdChpbnQq
KXBhcmFtLnB0cik7DQoJCQlicmVhazsNCg0KCQljYXNlIFBhcmFtVHlwZS5RV29y
ZDoNCgkJCWZ1bigqY2FzdChsb25nKilwYXJhbS5wdHIpOw0KCQkJYnJlYWs7DQoN
CgkJY2FzZSBQYXJhbVR5cGUuRmxvYXQ6DQoJCQlmdW4oKmNhc3QoZmxvYXQqKXBh
cmFtLnB0cik7DQoJCQlicmVhazsNCg0KCQljYXNlIFBhcmFtVHlwZS5Eb3VibGU6
DQoJCQlmdW4oKmNhc3QoZG91YmxlKilwYXJhbS5wdHIpOw0KCQkJYnJlYWs7DQoJ
CQkNCgkJY2FzZSBQYXJhbVR5cGUuUmVhbDoNCgkJCWZ1bigqY2FzdChyZWFsKilw
YXJhbS5wdHIpOw0KCQkJYnJlYWs7DQoJfQ0KfQ0KKy8NCg0KLy8gQ29udmVuaWVu
dCB0ZW1wbGF0ZXMgdG8gbWFwIGZyb20gdHlwZSBUIHRvIGNvcnJlc3BvbmRpbmcg
UGFyYW1UeXBlIGVudW0gZWxlbWVudA0KDQp0ZW1wbGF0ZSBpc1N0cnVjdFNpemUo
VCwgaW50IHNpemUpDQp7DQoJZW51bSBpc1N0cnVjdFNpemUgPSBpcyAoVCA9PSBz
dHJ1Y3QpICYmIFQuc2l6ZW9mID09IHNpemU7DQp9DQoNCnRlbXBsYXRlIFBhcmFt
VHlwZUZyb21UKFQpIGlmIChpcyAoVCA9PSBieXRlKSB8fCBpcyAoVCA9PSB1Ynl0
ZSkgfHwgaXMgKFQgPT0gY2hhcikgfHwgaXNTdHJ1Y3RTaXplIShULCAxKSkNCnsN
CglhbGlhcyBQYXJhbVR5cGUuQnl0ZSBQYXJhbVR5cGVGcm9tVDsNCn0NCg0KdGVt
cGxhdGUgUGFyYW1UeXBlRnJvbVQoVCkgaWYgKGlzIChUID09IHNob3J0KSB8fCBp
cyAoVCA9PSB1c2hvcnQpIHx8IGlzIChUID09IHdjaGFyKSB8fCBpc1N0cnVjdFNp
emUhKFQsIDIpKQ0Kew0KCWFsaWFzIFBhcmFtVHlwZS5Xb3JkIFBhcmFtVHlwZUZy
b21UOw0KfQ0KDQp0ZW1wbGF0ZSBQYXJhbVR5cGVGcm9tVChUKSBpZiAoaXMgKFQg
PT0gaW50KSB8fCBpcyAoVCA9PSB1aW50KSB8fCBpcyAoVCA9PSBkY2hhcikgfHwg
aXNTdHJ1Y3RTaXplIShULCA0KSkNCnsNCglhbGlhcyBQYXJhbVR5cGUuRFdvcmQg
UGFyYW1UeXBlRnJvbVQ7DQp9DQoNCnRlbXBsYXRlIFBhcmFtVHlwZUZyb21UKFQp
IGlmIChpcyAoVCA9PSBsb25nKSB8fCBpcyAoVCA9PSB1bG9uZykgfHwgaXNTdHJ1
Y3RTaXplIShULCA4KSB8fCBpcyAoVCA9PSBkZWxlZ2F0ZSkpDQp7DQoJYWxpYXMg
UGFyYW1UeXBlLlFXb3JkIFBhcmFtVHlwZUZyb21UOw0KfQ0KDQp0ZW1wbGF0ZSBQ
YXJhbVR5cGVGcm9tVChUKSBpZiAoaXMgKFQgPT0gZmxvYXQpKQ0Kew0KCWFsaWFz
IFBhcmFtVHlwZS5GbG9hdCBQYXJhbVR5cGVGcm9tVDsNCn0NCg0KdGVtcGxhdGUg
UGFyYW1UeXBlRnJvbVQoVCkgaWYgKGlzIChUID09IGRvdWJsZSkpDQp7DQoJYWxp
YXMgUGFyYW1UeXBlLkRvdWJsZSBQYXJhbVR5cGVGcm9tVDsNCn0NCg0KdGVtcGxh
dGUgUGFyYW1UeXBlRnJvbVQoVCkgaWYgKGlzIChUID09IHJlYWwpKQ0Kew0KCWFs
aWFzIFBhcmFtVHlwZS5SZWFsIFBhcmFtVHlwZUZyb21UOw0KfQ0KDQp0ZW1wbGF0
ZSBQYXJhbVR5cGVGcm9tVChUKSBpZiAoaXMgKFQgPT0gdm9pZCkpDQp7DQoJYWxp
YXMgUGFyYW1UeXBlLlZvaWQgUGFyYW1UeXBlRnJvbVQ7DQp9DQoNCnRlbXBsYXRl
IFBhcmFtVHlwZUZyb21UKFQpIGlmIChpc1BvaW50ZXIhKFQpKQ0Kew0KCWFsaWFz
IFBhcmFtVHlwZS5Qb2ludGVyIFBhcmFtVHlwZUZyb21UOw0KfQ==

------------m2pXpz6uLQtkfFRnyWpADe
Content-Disposition: attachment; filename=DynamicCall_test.d
Content-Type: application/octet-stream; name=DynamicCall_test.d
Content-Transfer-Encoding: Base64

aW1wb3J0IER5bmFtaWNDYWxsOw0KDQppbXBvcnQgc3RkLnN0ZGlvOw0KDQpQYXJh
bSBjcmVhdGVQYXJhbShUKShUIHZhbHVlKQ0Kew0KCS8vVCogcHRyID0gbmV3IFQ7
CS8vIEJVRyEgRG9lc24ndCB3b3JrIGZvciBkZWxlZ2F0ZSB0eXBlDQoJVCogcHRy
ID0gY2FzdChUKikobmV3IHZvaWRbVC5zaXplb2ZdKS5wdHI7DQoJKnB0ciA9IHZh
bHVlOw0KDQoJUGFyYW0gcGFyYW07DQoJcGFyYW0udHlwZSA9IFBhcmFtVHlwZUZy
b21UIShUKTsNCglwYXJhbS5wdHIgPSBjYXN0KHZvaWQqKXB0cjsNCg0KCXJldHVy
biBwYXJhbTsNCn0NCg0Kc3RydWN0IGIxDQp7DQoJY2hhciBjOw0KfQ0KDQpzdHJ1
Y3QgYjINCnsNCgl3Y2hhciB3Ow0KfQ0KDQpzdHJ1Y3QgYjQNCnsNCglkY2hhciBk
Ow0KfQ0KDQpyZWFsIGZvbyhieXRlIGIsIHNob3J0IHMsIGludCBpLCBsb25nIGws
IGZsb2F0IGYsIGRvdWJsZSBkLCByZWFsIHIsIGIxIGMsIGIyIHcsIGI0IGQyLCBp
bnQgZnVuY3Rpb24oKSBmdW5jLCBpbnQgZGVsZWdhdGUoKSBkZywgaW50KiBwdHIp
DQp7DQoJd3JpdGVsbihiICsgcyArIGkgKyBsICsgZiArIGQgKyByICsgYy5jICsg
dy53ICsgZDIuZCArIGZ1bmMoKSArIGRnKCkgKyAqcHRyKTsNCg0KCXJldHVybiAx
MzsNCn0NCg0KaW50IGZ1bmMoKQ0Kew0KCXJldHVybiA0MjsNCn0NCg0Kdm9pZCBt
YWluKCkNCnsNCglpbnQgZGcoKQ0KCXsNCgkJcmV0dXJuIDE0Ow0KCX0NCg0KCUNh
bGwgY2FsbDsNCgljYWxsLmZ1bmNwdHIgPSAmZm9vOw0KCQ0KCWludCogaSA9IG5l
dyBpbnQ7DQoJKmkgPSAxMjg7DQoNCgljYWxsLmlucHV0IH49IGNyZWF0ZVBhcmFt
IShieXRlKShjYXN0KGJ5dGUpMSk7DQoJY2FsbC5pbnB1dCB+PSBjcmVhdGVQYXJh
bSEoc2hvcnQpKDIwMCk7DQoJY2FsbC5pbnB1dCB+PSBjcmVhdGVQYXJhbSEoaW50
KSg0KTsNCgljYWxsLmlucHV0IH49IGNyZWF0ZVBhcmFtIShsb25nKShjYXN0KGxv
bmcpOCk7DQoJY2FsbC5pbnB1dCB+PSBjcmVhdGVQYXJhbSEoZmxvYXQpKDE2LjBm
KTsNCgljYWxsLmlucHV0IH49IGNyZWF0ZVBhcmFtIShkb3VibGUpKDMyLjApOw0K
CWNhbGwuaW5wdXQgfj0gY3JlYXRlUGFyYW0hKHJlYWwpKDY0LjApOw0KCWNhbGwu
aW5wdXQgfj0gY3JlYXRlUGFyYW0hKGIxKShiMSgxKSk7DQoJY2FsbC5pbnB1dCB+
PSBjcmVhdGVQYXJhbSEoYjIpKGIyKDIpKTsNCgljYWxsLmlucHV0IH49IGNyZWF0
ZVBhcmFtIShiNCkoYjQoNCkpOw0KCWNhbGwuaW5wdXQgfj0gY3JlYXRlUGFyYW0h
KGludCBmdW5jdGlvbigpKSgmZnVuYyk7DQoJY2FsbC5pbnB1dCB+PSBjcmVhdGVQ
YXJhbSEoaW50IGRlbGVnYXRlKCkpKCZkZyk7DQoJY2FsbC5pbnB1dCB+PSBjcmVh
dGVQYXJhbSEoaW50KikoaSk7DQoNCgljYWxsLm91dHB1dC50eXBlID0gUGFyYW1U
eXBlLlJlYWw7DQoJY2FsbC5vdXRwdXQucHRyID0gbmV3IHJlYWw7DQoNCgltYWtl
RHluYW1pY0NhbGwoJmNhbGwpOw0KCQ0KCXdyaXRlbG4oKmNhc3QocmVhbCopY2Fs
bC5vdXRwdXQucHRyKTsNCn0=

------------m2pXpz6uLQtkfFRnyWpADe--
Nov 20 2009
next sibling parent reply grauzone <none example.net> writes:
Denis Koroskin wrote:
 type-safe manner anymore (well, one could create a set of trampolines 
 for each of set of types involved in a call, but I don't think it's 
 reasonable or even possible; I'll look into it, too, though). That's why 

Yes, it is possible. You'll have to pass the method as alias template parameter. Then you get a tuple of the parameter types. You can foreach over that tuple and serialize/deserialize the actual values and read/write them from the tuple. You also can declare a nested function that does the actual call to the server's function. That delegate can have a type independent from the method, and thus can be stored in the non-template world. Basically like this (pseudo code): //client function to invoke a specific RPC //the parameters can be passed normally thanks to tuples void makeDynamicCall(Params...)(Stream stream, char[] method, Params p) { stream.write(method); //serialize the parameters foreach (int index, _; p) { auto val = p[index]; stream.write!(typeof(val))(val); } } alias void delegate(Stream) Stub; //registry of server functions Stub[char[]] stubs; //the server network code calls this on incomming RPC requests void receiveDynamicCall(Stream stream) { auto method = stream.read!(char[])(); stubs[method].call(stream); } //the server calls this on program initialization //he passes an alias to the server function, and its name void registerStub(alias Function)(char[] name) { //generate code to deserialize a RPC and to call the server void stub(Stream stream) { //you can get the param tuple of a function //Phobos2 and Tango should have something similar alias ParamTupleOfFunction!(Function) Params; //deserialize the arguments Params p; foreach (int index, _; p) { alias typeof(p[index]) PT; p[index] = stream.read!(PT)(); } //actually call the function Function(p); } stubs[name] = &stub; } It all can be typesafe and non-compiler specific. The question is; how much template bloat will this emit into the application binary? You have to consider the instantiations of Stream.read and Stream.write, too.
 Note that all the asm it uses is just 3 constructs: naked, ret and call! 
 Everything else is implemented with a help of compiler (i.e. pushing 
 arguments, grabbing result, stack alignment etc). My code is most 
 probably not very portable (I didn't test it on anything other that 
 Windows), it doesn't account x64 specifics etc, but it shouldn't be hard 
 to fix.

Wow, I'm surprised that this works. Actually, I'd *like* to do it this way, but the code is completely compiler and platform specific. Maybe it also depends from the compiler code generator's mood if it works. And if you want to deal with user-define types (structs), you're completely out of luck.
Nov 20 2009
parent reply "Denis Koroskin" <2korden gmail.com> writes:
------------OpL5kLfnlp5ns43ZoRNU32
Content-Type: text/plain; charset=koi8-r; format=flowed; delsp=yes
Content-Transfer-Encoding: 7bit

On Sat, 21 Nov 2009 06:54:11 +0300, grauzone <none example.net> wrote:

 Denis Koroskin wrote:
 type-safe manner anymore (well, one could create a set of trampolines  
 for each of set of types involved in a call, but I don't think it's  
 reasonable or even possible; I'll look into it, too, though). That's why

Yes, it is possible. You'll have to pass the method as alias template parameter. Then you get a tuple of the parameter types. You can foreach over that tuple and serialize/deserialize the actual values and read/write them from the tuple. You also can declare a nested function that does the actual call to the server's function. That delegate can have a type independent from the method, and thus can be stored in the non-template world. Basically like this (pseudo code): //client function to invoke a specific RPC //the parameters can be passed normally thanks to tuples void makeDynamicCall(Params...)(Stream stream, char[] method, Params p) { stream.write(method); //serialize the parameters foreach (int index, _; p) { auto val = p[index]; stream.write!(typeof(val))(val); } } alias void delegate(Stream) Stub; //registry of server functions Stub[char[]] stubs; //the server network code calls this on incomming RPC requests void receiveDynamicCall(Stream stream) { auto method = stream.read!(char[])(); stubs[method].call(stream); } //the server calls this on program initialization //he passes an alias to the server function, and its name void registerStub(alias Function)(char[] name) { //generate code to deserialize a RPC and to call the server void stub(Stream stream) { //you can get the param tuple of a function //Phobos2 and Tango should have something similar alias ParamTupleOfFunction!(Function) Params; //deserialize the arguments Params p; foreach (int index, _; p) { alias typeof(p[index]) PT; p[index] = stream.read!(PT)(); } //actually call the function Function(p); } stubs[name] = &stub; } It all can be typesafe and non-compiler specific. The question is; how much template bloat will this emit into the application binary? You have to consider the instantiations of Stream.read and Stream.write, too.

I thought about that, too. And then I thought: 1) you can move deserialization out of this method (my serializer doesn't need type hints to deserialize data). 2) you can reuse the trampoline for those functions that have same type of input argument (less bloat) 3) you can create a trampoline for each type, not for each set of types (N trampolines instead of N! in worst case) One more step and you don't even need for those trampolines (just carry some type information and translate call to trampoline into a switch). That's how I ended up with my implementation.
 Note that all the asm it uses is just 3 constructs: naked, ret and  
 call! Everything else is implemented with a help of compiler (i.e.  
 pushing arguments, grabbing result, stack alignment etc). My code is  
 most probably not very portable (I didn't test it on anything other  
 that Windows), it doesn't account x64 specifics etc, but it shouldn't  
 be hard to fix.

Wow, I'm surprised that this works. Actually, I'd *like* to do it this way, but the code is completely compiler and platform specific. Maybe it also depends from the compiler code generator's mood if it works. And if you want to deal with user-define types (structs), you're completely out of luck.

Why? It works well with custom structs, too. There is a test case attached, try to run in, it should work. One of the reasons I posted the code is because I'm not sure it is correct and that it will always work (on a given platform). I'm not an ASM guru, so comments would be invaluable. For example, I use the following trick to push arguments to stack: // A helper function that pushes an argument to stack in a type-safe manner // extern (System) is used so that argument isn't passed via EAX // does it work the same way in Linux? Or Linux uses __cdecl? // There must be other way to pass all the arguments on stack, but this one works well so far // Beautiful, isn't it? extern (System) void arg(T)(T arg) { asm { naked; ret; } } // A helper function that pushes an argument to stack in a type-safe manner // Allowed to pass argumet via EAX (that's why it's extern (D)) void lastArg(T)(T arg) { asm { naked; ret; } } Explanation: when one of these functions are called, arguments to them are pushed to stack and/or target registers properly (compiler does it for me automatically). By convention, extern (D) and extern (Windows) aka __stdcall function pop their arguments from the stack on their own. Naked function (asm naked) lack prolog and epilog (i.e. they don't pop arguments and restore stack). As a result, these function push their arguments to stack the way compiler wants them while still not being compiler specific. lastArg uses extern (D) calling convention that allows passing last argument in EAX (this is specified in http://www.digitalmars.com/d/2.0/abi.html) How safe it is? How portable it is? As far as I know all major compilers support naked functions (Microsoft C++ compiler: _declspec( naked ), GCC: __attribute__(naked)) This is 2 out of 3 uses of asm in my code. The third one is: void* functptr = ...; asm { call funcptr; } I think this is also implementable on other platforms. I updated code to work with Tango, could anybody please try to run it on Linux x86/x86_64 (ldc)? An output should be: 516.00 13.00 ------------OpL5kLfnlp5ns43ZoRNU32 Content-Disposition: attachment; filename=DynamicCall.d Content-Type: application/octet-stream; name=DynamicCall.d Content-Transfer-Encoding: Base64 bW9kdWxlIER5bmFtaWNDYWxsOw0KDQppbXBvcnQgdGFuZ28uY29yZS5UcmFpdHM7 DQoNCnRlbXBsYXRlIGlzUG9pbnRlcihUKQ0Kew0KCWNvbnN0IGJvb2wgaXNQb2lu dGVyID0gaXNQb2ludGVyVHlwZSEoVCk7DQp9DQoNCmVudW0gUGFyYW1UeXBlDQp7 DQoJLy8gTm8gcmV0dXJuIHZhbHVlDQoJVm9pZCwNCg0KCS8vIFNUMA0KCUZsb2F0 LA0KCURvdWJsZSwNCglSZWFsLA0KDQoJLy8gRUFYDQoJQnl0ZSwNCglXb3JkLA0K CURXb3JkLA0KCVBvaW50ZXIsCQkvLyBjb3VsZCBwcm9iYWJseSBiZSByZW1vdmVk LiBSZXNvbHZlIHRvIERXb3JkL1FXb3JkIGluc3RlYWQNCgkvLyBBQXJyYXksIAkJ Ly8gaXNuJ3QgdGVzdGVkIChtZXJnZSB3aXRoIFBvaW50ZXI/KQ0KDQoJLy9FRFgs RUFYDQoJUVdvcmQsDQoJREFycmF5LAkJIC8vIGlzbid0IHRlc3RlZA0KDQoJLy8g SGlkZGVuIHBvaW50ZXINCglIaWRkZW5fUG9pbnRlciwJLy8gZm9yIHJldHVybmlu ZyBsYXJnZSAoPjggYnl0ZXMpIHN0cnVjdHMsIG5vdCB0ZXN0ZWQgeWV0DQp9DQoN CnN0cnVjdCBQYXJhbQ0Kew0KCVBhcmFtVHlwZSB0eXBlOwkvLyB0eXBlIG9mIHZh bHVlDQoJdm9pZCogcHRyOwkJLy8gcG9pbnRlciB0byB2YWx1ZQ0KfQ0KDQovLyBU aGlzIHN0cnVjdCBkZXNjcmliZXMgZXZlcnl0aGluZyBuZWVkZWQgdG8gbWFrZSBh IGNhbGwNCnN0cnVjdCBDYWxsDQp7DQoJUGFyYW1bXSBpbnB1dDsJLy8gc2V0IG9m IGlucHV0IGFyZ3VtZW50cw0KCVBhcmFtIG91dHB1dDsJLy8gcmVzdWx0IGluZm8N Cgl2b2lkKiBmdW5jcHRyOwkvLyBmdW5jdGlvbiBwb2ludGVyDQp9DQoNCi8vIG1h a2VzIGEgY2FsbCwgc3RvcmVzIHJlc3VsdCBpbiBjYWxsLm91cHV0DQp2b2lkIG1h a2VEeW5hbWljQ2FsbChDYWxsKiBjYWxsKQ0Kew0KCXN3aXRjaCAoY2FsbC5vdXRw dXQudHlwZSkgew0KCQljYXNlIFBhcmFtVHlwZS5Wb2lkOg0KCQkJX21ha2VDYWxs ISh2b2lkKShjYWxsKTsNCgkJCWJyZWFrOw0KDQoJCWNhc2UgUGFyYW1UeXBlLlBv aW50ZXI6DQoJCQltYWtlQ2FsbCEodm9pZCopKGNhbGwpOw0KCQkJYnJlYWs7DQoN CgkJY2FzZSBQYXJhbVR5cGUuQnl0ZToNCgkJCW1ha2VDYWxsIShieXRlKShjYWxs KTsNCgkJCWJyZWFrOw0KCQkJDQoJCWNhc2UgUGFyYW1UeXBlLldvcmQ6DQoJCQlt YWtlQ2FsbCEoc2hvcnQpKGNhbGwpOw0KCQkJYnJlYWs7DQoJCQkNCgkJY2FzZSBQ YXJhbVR5cGUuRFdvcmQ6DQoJCQltYWtlQ2FsbCEoaW50KShjYWxsKTsNCgkJCWJy ZWFrOw0KCQkJDQoJCWNhc2UgUGFyYW1UeXBlLlFXb3JkOg0KCQkJbWFrZUNhbGwh KGxvbmcpKGNhbGwpOw0KCQkJYnJlYWs7DQoNCgkJY2FzZSBQYXJhbVR5cGUuRmxv YXQ6DQoJCQltYWtlQ2FsbCEoZmxvYXQpKGNhbGwpOw0KCQkJYnJlYWs7DQoNCgkJ Y2FzZSBQYXJhbVR5cGUuRG91YmxlOg0KCQkJbWFrZUNhbGwhKGRvdWJsZSkoY2Fs bCk7DQoJCQlicmVhazsNCgkJCQ0KCQljYXNlIFBhcmFtVHlwZS5SZWFsOg0KCQkJ bWFrZUNhbGwhKHJlYWwpKGNhbGwpOw0KCQkJYnJlYWs7DQoJfQ0KfQ0KDQovLyBo ZWxwZXIgZnVuY3Rpb24gdG8gc2F2ZSBzb21lIHR5cGluZw0Kdm9pZCBtYWtlQ2Fs bChUKShDYWxsKiBjYWxsKQ0Kew0KCSpjYXN0KFQqKWNhbGwub3V0cHV0LnB0ciA9 IF9tYWtlQ2FsbCEoVCkoY2FsbCk7DQp9DQoNClQgX21ha2VDYWxsKFQpKENhbGwq IGNhbGwpDQp7DQoJdm9pZCogZnVuY3B0ciA9IGNhbGwuZnVuY3B0cjsNCgl2b2lk KiBhcmdwdHI7DQoNCglpbnQgbnVtQXJncyA9IGNhbGwuaW5wdXQubGVuZ3RoOw0K CQ0KCWlmIChudW1BcmdzICE9IDApIHsJLy8gdGhpcyBjaGVjayBpcyBuZWVkZWQg YmVjYXVzZSBsYXN0IHBhcmFtZXRlciBpcyBwYXNzZWQgaW4gRUFYIChpZiBwb3Nz aWJsZSkNCgkJUGFyYW0qIHBhcmFtID0gY2FsbC5pbnB1dC5wdHI7DQoJCQ0KCQkv LyBpdGVyYXRlIG92ZXIgZmlyc3QgbnVtQXJncy0xIGFyZ3VtZW50cw0KCQlmb3Ig KCA7IC0tbnVtQXJnczsgKytwYXJhbSkgew0KCQkJYXJncHRyID0gcGFyYW0ucHRy Ow0KCQkJc3dpdGNoIChwYXJhbS50eXBlKSB7DQoJCQkJY2FzZSBQYXJhbVR5cGUu Qnl0ZToJLy8gcHVzaCBieXRlDQoJCQkJCWFyZygqY2FzdChieXRlKilhcmdwdHIp Ow0KCQkJCQlicmVhazsNCgkJCQkJDQoJCQkJY2FzZSBQYXJhbVR5cGUuV29yZDoJ Ly8gcHVzaCB3b3JkDQoJCQkJCWFyZygqY2FzdChzaG9ydCopYXJncHRyKTsNCgkJ CQkJYnJlYWs7DQoNCgkJCQljYXNlIFBhcmFtVHlwZS5Qb2ludGVyOg0KCQkJCWNh c2UgUGFyYW1UeXBlLkRXb3JkOgkvLyBwdXNoIGR3b3JkDQoJCQkJCWFyZygqY2Fz dChpbnQqKWFyZ3B0cik7DQoJCQkJCWJyZWFrOw0KDQoJCQkJY2FzZSBQYXJhbVR5 cGUuUVdvcmQ6CS8vIHB1c2ggcXdvcmQNCgkJCQkJYXJnKCpjYXN0KGxvbmcqKWFy Z3B0cik7DQoJCQkJCWJyZWFrOw0KDQoJCQkJY2FzZSBQYXJhbVR5cGUuRmxvYXQ6 CS8vIHB1c2ggZmxvYXQNCgkJCQkJYXJnKCpjYXN0KGZsb2F0KilhcmdwdHIpOw0K CQkJCQlicmVhazsNCg0KCQkJCWNhc2UgUGFyYW1UeXBlLkRvdWJsZToJLy8gcHVz aCBkb3VibGUNCgkJCQkJYXJnKCpjYXN0KGRvdWJsZSopYXJncHRyKTsNCgkJCQkJ YnJlYWs7DQoJCQkJCQ0KCQkJCWNhc2UgUGFyYW1UeXBlLlJlYWw6CS8vIHB1c2gg cmVhbA0KCQkJCQlhcmcoKmNhc3QocmVhbCopYXJncHRyKTsNCgkJCQkJYnJlYWs7 DQoJCQl9DQoJCX0NCg0KCQkvLyBzYW1lIGFzIGFib3ZlIGJ1dCBwYXNzZXMgaW4g RUFYIGlmIHBvc3NpYmxlDQoNCgkJYXJncHRyID0gcGFyYW0ucHRyOw0KCQlzd2l0 Y2ggKHBhcmFtLnR5cGUpIHsNCgkJCWNhc2UgUGFyYW1UeXBlLkJ5dGU6DQoJCQkJ bGFzdEFyZygqY2FzdChieXRlKilhcmdwdHIpOw0KCQkJCWJyZWFrOw0KCQkJCQ0K CQkJY2FzZSBQYXJhbVR5cGUuV29yZDoNCgkJCQlsYXN0QXJnKCpjYXN0KHNob3J0 KilhcmdwdHIpOw0KCQkJCWJyZWFrOw0KCQkJDQoJCQl2ZXJzaW9uKFg4Nl82NCkg ew0KCQkJfSBlbHNlIHZlcnNpb24gKFg4Nikgew0KCQkJCWNhc2UgUGFyYW1UeXBl LlBvaW50ZXI6DQoJCQl9DQoJCQljYXNlIFBhcmFtVHlwZS5EV29yZDoNCgkJCQls YXN0QXJnKCpjYXN0KGludCopYXJncHRyKTsNCgkJCQlicmVhazsNCg0KCQkJdmVy c2lvbihYODZfNjQpIHsNCgkJCQljYXNlIFBhcmFtVHlwZS5Qb2ludGVyOg0KCQkJ fQ0KCQkJY2FzZSBQYXJhbVR5cGUuUVdvcmQ6DQoJCQkJbGFzdEFyZygqY2FzdChs b25nKilhcmdwdHIpOw0KCQkJCWJyZWFrOw0KDQoJCQljYXNlIFBhcmFtVHlwZS5G bG9hdDoNCgkJCQlsYXN0QXJnKCpjYXN0KGZsb2F0KilhcmdwdHIpOw0KCQkJCWJy ZWFrOw0KDQoJCQljYXNlIFBhcmFtVHlwZS5Eb3VibGU6DQoJCQkJbGFzdEFyZygq Y2FzdChkb3VibGUqKWFyZ3B0cik7DQoJCQkJDQoJCQljYXNlIFBhcmFtVHlwZS5S ZWFsOg0KCQkJCWxhc3RBcmcoKmNhc3QocmVhbCopYXJncHRyKTsNCgkJfQ0KCX0N Cg0KCWFzbSB7DQoJCS8vIGNhbGwgaXQhDQoJCWNhbGwgZnVuY3B0cjsNCgl9DQp9 DQoNCi8vIEEgaGVscGVyIGZ1bmN0aW9uIHRoYXQgcHVzaGVzIGFuIGFyZ3VtZW50 IHRvIHN0YWNrIGluIGEgdHlwZS1zYWZlIG1hbm5lcg0KLy8gZXh0ZXJuIChTeXN0 ZW0pIGlzIHVzZWQgc28gdGhhdCBhcmd1bWVudCBpc24ndCBwYXNzZWQgdmlhIEVB WA0KLy8gZG9lcyBpdCB3b3JrIHRoZSBzYW1lIHdheSBpbiBMaW51eD8gT3IgTGlu dXggdXNlcyBfX2NkZWNsPw0KLy8gVGhlcmUgbXVzdCBiZSBvdGhlciB3YXkgdG8g cGFzcyBhbGwgdGhlIGFyZ3VtZW50cyBvbiBzdGFjaywgYnV0IHRoaXMgb25lIHdv cmtzIHdlbGwgc28gZmFyDQovLyBCZWF1dGlmdWwsIGlzbid0IGl0Pw0KZXh0ZXJu IChTeXN0ZW0pIHZvaWQgYXJnKFQpKFQgYXJnKQ0Kew0KCWFzbSB7DQoJCW5ha2Vk Ow0KCQlyZXQ7DQoJfQ0KfQ0KDQovLyBBIGhlbHBlciBmdW5jdGlvbiB0aGF0IHB1 c2hlcyBhbiBhcmd1bWVudCB0byBzdGFjayBpbiBhIHR5cGUtc2FmZSBtYW5uZXIN Ci8vIEFsbG93ZWQgdG8gcGFzcyBhcmd1bWV0IHZpYSBFQVggKHRoYXQncyB3aHkg aXQncyBleHRlcm4gKEQpKQ0Kdm9pZCBsYXN0QXJnKFQpKFQgYXJnKQ0Kew0KCWFz bSB7DQoJCW5ha2VkOw0KCQlyZXQ7DQoJfQ0KfQ0KDQovLyBDb252ZW5pZW50IHRl bXBsYXRlcyB0byBtYXAgZnJvbSB0eXBlIFQgdG8gY29ycmVzcG9uZGluZyBQYXJh bVR5cGUgZW51bSBlbGVtZW50DQoNCnRlbXBsYXRlIGlzU3RydWN0U2l6ZShULCBp bnQgc2l6ZSkNCnsNCgljb25zdCBpbnQgaXNTdHJ1Y3RTaXplID0gaXMgKFQgPT0g c3RydWN0KSAmJiBULnNpemVvZiA9PSBzaXplOw0KfQ0KDQp0ZW1wbGF0ZSBQYXJh bVR5cGVGcm9tVChUKQ0Kew0KCXN0YXRpYyBpZiAoaXMgKFQgPT0gYnl0ZSkgfHwg aXMgKFQgPT0gdWJ5dGUpIHx8IGlzIChUID09IGNoYXIpIHx8IGlzU3RydWN0U2l6 ZSEoVCwgMSkpIHsNCgkJY29uc3QgUGFyYW1UeXBlIFBhcmFtVHlwZUZyb21UID0g UGFyYW1UeXBlLkJ5dGU7DQoJfSBlbHNlIHN0YXRpYyBpZiAoaXMgKFQgPT0gc2hv cnQpIHx8IGlzIChUID09IHVzaG9ydCkgfHwgaXMgKFQgPT0gd2NoYXIpIHx8IGlz U3RydWN0U2l6ZSEoVCwgMikpIHsNCgkJY29uc3QgUGFyYW1UeXBlIFBhcmFtVHlw ZUZyb21UID0gUGFyYW1UeXBlLldvcmQ7DQoJfSBlbHNlIHN0YXRpYyBpZiAoaXMg KFQgPT0gaW50KSB8fCBpcyAoVCA9PSB1aW50KSB8fCBpcyAoVCA9PSBkY2hhcikg fHwgaXNTdHJ1Y3RTaXplIShULCA0KSkgew0KCQljb25zdCBQYXJhbVR5cGUgUGFy YW1UeXBlRnJvbVQgPSBQYXJhbVR5cGUuRFdvcmQ7DQoJfSBlbHNlIHN0YXRpYyBp ZiAoaXMgKFQgPT0gbG9uZykgfHwgaXMgKFQgPT0gdWxvbmcpIHx8IGlzU3RydWN0 U2l6ZSEoVCwgOCkgfHwgaXMgKFQgPT0gZGVsZWdhdGUpKSB7DQoJCWNvbnN0IFBh cmFtVHlwZSBQYXJhbVR5cGVGcm9tVCA9IFBhcmFtVHlwZS5RV29yZDsNCgl9IGVs c2Ugc3RhdGljIGlmIChpcyAoVCA9PSBmbG9hdCkpIHsNCgkJY29uc3QgUGFyYW1U eXBlIFBhcmFtVHlwZUZyb21UID0gUGFyYW1UeXBlLkZsb2F0Ow0KCX0gZWxzZSBz dGF0aWMgaWYgKGlzIChUID09IGRvdWJsZSkpIHsNCgkJY29uc3QgUGFyYW1UeXBl IFBhcmFtVHlwZUZyb21UID0gUGFyYW1UeXBlLkRvdWJsZTsNCgl9IGVsc2Ugc3Rh dGljIGlmIChpcyAoVCA9PSByZWFsKSkgew0KCQljb25zdCBQYXJhbVR5cGUgUGFy YW1UeXBlRnJvbVQgPSBQYXJhbVR5cGUuUmVhbDsNCgl9IGVsc2Ugc3RhdGljIGlm IChpcyAoVCA9PSB2b2lkKSkgew0KCQljb25zdCBQYXJhbVR5cGUgUGFyYW1UeXBl RnJvbVQgPSBQYXJhbVR5cGUuVm9pZDsNCgl9IGVsc2Ugc3RhdGljIGlmIChpc1Bv aW50ZXIhKFQpKSB7DQoJCWNvbnN0IFBhcmFtVHlwZSBQYXJhbVR5cGVGcm9tVCA9 IFBhcmFtVHlwZS5Qb2ludGVyOw0KCX0NCn0= ------------OpL5kLfnlp5ns43ZoRNU32 Content-Disposition: attachment; filename=DynamicCall_test.d Content-Type: application/octet-stream; name=DynamicCall_test.d Content-Transfer-Encoding: Base64 aW1wb3J0IER5bmFtaWNDYWxsOw0KDQppbXBvcnQgdGFuZ28uaW8uU3Rkb3V0Ow0K DQpQYXJhbSBjcmVhdGVQYXJhbShUKShUIHZhbHVlKQ0Kew0KCS8vVCogcHRyID0g bmV3IFQ7CS8vIEJVRyEgRG9lc24ndCB3b3JrIGZvciBkZWxlZ2F0ZSB0eXBlDQoJ VCogcHRyID0gY2FzdChUKikobmV3IHZvaWRbVC5zaXplb2ZdKS5wdHI7DQoJKnB0 ciA9IHZhbHVlOw0KDQoJUGFyYW0gcGFyYW07DQoJcGFyYW0udHlwZSA9IFBhcmFt VHlwZUZyb21UIShUKTsNCglwYXJhbS5wdHIgPSBjYXN0KHZvaWQqKXB0cjsNCg0K CXJldHVybiBwYXJhbTsNCn0NCg0Kc3RydWN0IGIxDQp7DQoJY2hhciBjOw0KfQ0K DQpzdHJ1Y3QgYjINCnsNCgl3Y2hhciB3Ow0KfQ0KDQpzdHJ1Y3QgYjQNCnsNCglk Y2hhciBkOw0KfQ0KDQpyZWFsIGZvbyhieXRlIGIsIHNob3J0IHMsIGludCBpLCBs b25nIGwsIGZsb2F0IGYsIGRvdWJsZSBkLCByZWFsIHIsIGIxIGMsIGIyIHcsIGI0 IGQyLCBpbnQgZnVuY3Rpb24oKSBmdW5jLCBpbnQgZGVsZWdhdGUoKSBkZywgaW50 KiBwdHIpDQp7DQoJU3Rkb3V0KGIgKyBzICsgaSArIGwgKyBmICsgZCArIHIgKyBj LmMgKyB3LncgKyBkMi5kICsgZnVuYygpICsgZGcoKSArICpwdHIpLm5ld2xpbmU7 DQoJcmV0dXJuIDEzOw0KfQ0KDQppbnQgZnVuYygpDQp7DQoJcmV0dXJuIDQyOw0K fQ0KDQp2b2lkIG1haW4oKQ0Kew0KCWludCBkZygpDQoJew0KCQlyZXR1cm4gMTQ7 DQoJfQ0KDQoJQ2FsbCBjYWxsOw0KCWNhbGwuZnVuY3B0ciA9ICZmb287DQoJDQoJ aW50KiBpID0gbmV3IGludDsNCgkqaSA9IDEyODsNCg0KCWNhbGwuaW5wdXQgfj0g Y3JlYXRlUGFyYW0hKGJ5dGUpKGNhc3QoYnl0ZSkxKTsNCgljYWxsLmlucHV0IH49 IGNyZWF0ZVBhcmFtIShzaG9ydCkoMjAwKTsNCgljYWxsLmlucHV0IH49IGNyZWF0 ZVBhcmFtIShpbnQpKDQpOw0KCWNhbGwuaW5wdXQgfj0gY3JlYXRlUGFyYW0hKGxv bmcpKGNhc3QobG9uZyk4KTsNCgljYWxsLmlucHV0IH49IGNyZWF0ZVBhcmFtIShm bG9hdCkoMTYuMGYpOw0KCWNhbGwuaW5wdXQgfj0gY3JlYXRlUGFyYW0hKGRvdWJs ZSkoMzIuMCk7DQoJY2FsbC5pbnB1dCB+PSBjcmVhdGVQYXJhbSEocmVhbCkoNjQu MCk7DQoJY2FsbC5pbnB1dCB+PSBjcmVhdGVQYXJhbSEoYjEpKGIxKDEpKTsNCglj YWxsLmlucHV0IH49IGNyZWF0ZVBhcmFtIShiMikoYjIoMikpOw0KCWNhbGwuaW5w dXQgfj0gY3JlYXRlUGFyYW0hKGI0KShiNCg0KSk7DQoJY2FsbC5pbnB1dCB+PSBj cmVhdGVQYXJhbSEoaW50IGZ1bmN0aW9uKCkpKCZmdW5jKTsNCgljYWxsLmlucHV0 IH49IGNyZWF0ZVBhcmFtIShpbnQgZGVsZWdhdGUoKSkoJmRnKTsNCgljYWxsLmlu cHV0IH49IGNyZWF0ZVBhcmFtIShpbnQqKShpKTsNCg0KCWNhbGwub3V0cHV0LnR5 cGUgPSBQYXJhbVR5cGUuUmVhbDsNCgljYWxsLm91dHB1dC5wdHIgPSBuZXcgcmVh bDsNCg0KCW1ha2VEeW5hbWljQ2FsbCgmY2FsbCk7DQoJDQoJU3Rkb3V0KCpjYXN0 KHJlYWwqKWNhbGwub3V0cHV0LnB0cikubmV3bGluZTsNCn0= ------------OpL5kLfnlp5ns43ZoRNU32--
Nov 21 2009
parent grauzone <none example.net> writes:
Denis Koroskin wrote:
 I thought about that, too. And then I thought:
 1) you can move deserialization out of this method (my serializer 
 doesn't need type hints to deserialize data).
 2) you can reuse the trampoline for those functions that have same type 
 of input argument (less bloat)
 3) you can create a trampoline for each type, not for each set of types 
 (N trampolines instead of N! in worst case)

That would be interesting.
 Wow, I'm surprised that this works. Actually, I'd *like* to do it this 
 way, but the code is completely compiler and platform specific. Maybe 
 it also depends from the compiler code generator's mood if it works. 
 And if you want to deal with user-define types (structs), you're 
 completely out of luck.

Why? It works well with custom structs, too. There is a test case attached, try to run in, it should work.

And you have to special case this in very awkward ways. What if a struct is larger than 8 bytes? This whole approach is so shaky, I don't trust it one bit. It would be another thing if all this would be part of the runtime core (and the compiler implementer makes sure it works).
 One of the reasons I posted the code is because I'm not sure it is 
 correct and that it will always work (on a given platform).
 I'm not an ASM guru, so comments would be invaluable.

Neither am I, but I guess it will be very hard to emulate calling conventions on non-x86 32 bit platforms. E.g. on amd64, several arguments are passed in registers (AFAIK). In any case, you trust the compiler not to insert additional code and not to touch the stack. Maybe it works with dmd, but what about ldc or gdc? Well, let's see if anyone can test it... Now, this doesn't really matter. You can always rewrite it as asm code. And in asm, you definitely can do such dynamic calls. One issue is that you need the static signature of the RPC target for type checking. What are you going to do about this?
 I updated code to work with Tango, could anybody please try to run it on 
 Linux x86/x86_64 (ldc)? An output should be:
 516.00
 13.00

Segfaulted on x86 32 bit / Tango / Linux.
Nov 21 2009
prev sibling next sibling parent Rory McGuire <rjmcguire gmail.com> writes:
"Denis Koroskin" <2korden gmail.com> wrote:
 
 "Denis Koroskin" <2korden gmail.com> wrote:
 ...

cut
 ...

Why did you use asm etc...? I made a de/serializer that just uses templates and static ifs. it doesn't do serialization of delegates etc... but I don't know that moving executable code across would be safe (I think java can do it, but then it has a vm). mine does: int, long, double, string, object, struct. I can post the code if you want. So far I have only used it to present the methods of an object as a kind of server, with a client being able to connect to it. Was inspired by hessiand on dsource.org. Can post code if you would like to see. I posted the automatic object wrapping code a couple of days ago as a simple example of template usage Thread: "Metaprogramming in D: Some real world examples". -Rory
Nov 21 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 21 Nov 2009 12:42:35 +0300, Rory McGuire <rjmcguire gmail.com>  
wrote:

 "Denis Koroskin" <2korden gmail.com> wrote:

 "Denis Koroskin" <2korden gmail.com> wrote:
 ...

cut
 ...

Why did you use asm etc...? I made a de/serializer that just uses templates and static ifs. it doesn't do serialization of delegates etc... but I don't know that moving executable code across would be safe (I think java can do it, but then it has a vm). mine does: int, long, double, string, object, struct. I can post the code if you want. So far I have only used it to present the methods of an object as a kind of server, with a client being able to connect to it. Was inspired by hessiand on dsource.org. Can post code if you would like to see. I posted the automatic object wrapping code a couple of days ago as a simple example of template usage Thread: "Metaprogramming in D: Some real world examples". -Rory

You missed the point. I only used asm to invoke function on remote side. Serialization is done purely with templates.
Nov 21 2009
prev sibling parent Rory McGuire <rjmcguire gmail.com> writes:
"Denis Koroskin" <2korden gmail.com> wrote:
 
 On Sat, 21 Nov 2009 12:42:35 +0300, Rory McGuire <rjmcguire gmail.com>  
 wrote:
 
 "Denis Koroskin" <2korden gmail.com> wrote:

 "Denis Koroskin" <2korden gmail.com> wrote:
 ...

cut
 ...

Why did you use asm etc...? I made a de/serializer that just uses templates and static ifs. it doesn't do serialization of delegates etc... but I don't know that moving executable code across would be safe (I think java can do it, but then it has a vm). mine does: int, long, double, string, object, struct. I can post the code if you want. So far I have only used it to present the methods of an object as a kind of server, with a client being able to connect to it. Was inspired by hessiand on dsource.org. Can post code if you would like to see. I posted the automatic object wrapping code a couple of days ago as a simple example of template usage Thread: "Metaprogramming in D: Some real world examples". -Rory

You missed the point. I only used asm to invoke function on remote side. Serialization is done purely with templates.

:) I thought I had missed the point thats why I asked :D. On the Server side I used a similar method that the guy who wrote hessiand used for calling the objects methods, my code: module servers.ObjectSocketServer; //import serialize.ISerializer; import transports.ITransport; import transports.SocketTransport; import tango.core.Exception; version (Tango) { alias char[] string; import tango.net.Socket; import tango.io.Stdout; import tango.core.Traits; // alias ParameterTupleOf ParameterTypeTuple; } class ObjectSocketServer(T,C) { Socket request_conn; T serializer; ITransport transport; C instance; IMethod[string] methods; this(Socket request_conn) { instance = new C(); transport = new SocketTransport(request_conn); serializer = new T(transport); this.request_conn = request_conn; } ~this() { request_conn = null; serializer = null; transport = null; foreach (key, method; methods) { methods.remove(key); } instance = null; } void close() { try { if (request_conn !is null) { request_conn.shutdown(SocketShutdown.BOTH); request_conn.detach(); } } catch (Exception e) {} } /+ void expose(char[] method)() { pragma(msg, mixin(MethodType_mixin!(method)())); } +/ void expose(alias M)() { const string name = NameOfFunc!(M); //pragma(msg, "expose: "~ name); pragma(msg, "expose:\n"~ GenMethod!(C, name)); methods[name] = mixin(GenMethod!(C, name)); } void run() { scope(exit) close(); try { Stdout("run").newline; int i; while (true) { //Stdout("again").newline; auto method = serializer.read!(string)(); //Stdout("method = "~ method).newline; try { if (!(method in methods)) throw new Exception("Not Found"); methods[method](serializer, instance); } catch (Exception e) { e.writeOut((char[] str) { Stdout(str).newline;}); Stdout("Caught exception must serialize: ")(e.line)(":") (e.file)(" ")(e.toString).newline; } Stdout(i)("\r")(); i++; } Stdout().newline; } catch (Exception e) { Stdout("Caught exception: ")(e.toString).newline; } } interface IMethod { string getName(); void opCall(T, C); } } // this would be inside the class but cant be string GenMethod(alias C, string name)() { string ret = "new class IMethod {\n"; ret ~= "\tstring getName() { return \""~ name ~"\"; }\n"; ret ~= "\tvoid opCall(T serializer, C instance) {\n"; ret ~= "\t\tserializer.write(instance."~name~"(serializer.read! ("~ParameterTupleOf!(mixin(MethodType_mixin!(name)()))[0].stringof~")()));\n"; foreach (i,p; ParameterTupleOf!(mixin(MethodType_mixin!(name)()))[1..$]) { ret ~= "\t\tserializer.write(instance."~name~"(serializer.read! ("~p.stringof~")()));\n"; } ret ~= "\t\tserializer.getTransport().flush();\n"; ret ~= "\t}\n"; ret ~= "}\n"; return ret; } public template NameOfFunc(alias f) { version (LDC) { const char[] NameOfFunc = (&f).stringof[1 .. $]; } else { const char[] NameOfFunc = (&f).stringof[2 .. $]; } } string MethodType_mixin(string method)() { return "typeof(&C.init."~ method ~")"; }
Nov 22 2009