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.

Reply via email to