RPC - Any ideas on a better approach

13 Dec 2009

The mbed objects support RPC through a text interface via a base class. The main objective of putting this in was to see if the concept would work. I always liked the idea of having an RPC interface to the objects, then being able to put it over any transport mechanism (in particular, http), and it seems to be quite effective.

However, I'm not really happy with the way it has to be manually implemented in to objects in quite an ugly way. I was therefore wondering if anyone had any experience with RPC generators or ideas about introspection/reflection techniques that could work in C/C++ to be able to make it all a bit cleaner?

Simon

22 Dec 2009 . Edited: 22 Dec 2009

(removed runaway post)

22 Dec 2009 . Edited: 22 Dec 2009

I see no responses here, which does not surprise me actually. I mentioned my experience somewhere else here. The only realistic approach that I've encountered is IDL (interface description language) and some code-generation tools. M$ has MIDL tool, which is Windows-centric (surprisingly! ;-). IDL could be conceivably done in a platform-independent way, and I was able to get rid of #include "windows.h" line in .idl files, but the code generator was too much complexity to work through.

Unfortunately C/C++ lacks expressive constructs and introspection to have any other RPC method besides either external code-generating tool (MIDL or similar), or data-driven implementation that you did (which is simple but powerful, which is impressive). The only other "simplification" I can think of is using macros to wrap constructors and member functions that will automatically translate into the given data structures. Code will look like something like this:

 

myrpc.h

#include "rpc.h"

RPC_CLASS(myrpc, PinName, char*); // This translates to class myrpc { void myrpc(PinName,char*);
RPC_CONSTRUCT_ALT(myrpc, PinName, PinName, char*);    // Another constructor
RPC_CLASS_FUNC(/*return*/ void, /*class*/ myrpc,  /*func*/ myfunc, /*args*/ int, char*, ...);
 // Other class properties and methods go here
... 
END_RPC_CLASS();    // This translates to  virtual const struct rpc_method *get_rpc_methods(); static struct rpc_class *get_rpc_class(); }

 

myrpc.cpp

#include "myrpc.h"
// Implementation of constructor: 
myrpc::myrpc(PinName pin,char* name) : Base(name) , ... { ... }

// Implementation of member function: 
void myrpc::myfunc(int number, char* string) {...}
 
RPC_CLASS_WRAPPER(myrpc);   // This translates into const rpc_method *myrpc::get_rpc_methods() {...} and rpc_class *myrpc::get_rpc_class() {}

 

This is a crude example without internal details of macros. As you could see the overhead is one line in .cpp file and wrapper macros on class and functions. The devil is in the details of course - the macros will have to be quite inventive to use preprocessor to levels not used by most people in order to generate both the functions and the data structures (I would use #define __RPC_DATA__ followed by #include of the header file again inside RPC_CLASS_WRAPPER macro, of course if preprocessor does multi-pass processing properly), but it can somewhat streamline the code and hide the nasty data structures from the eye. I gather that's the objective.

Oh, one last thing... I don't think you can easily marshall more complicated structures through RPC and deal with endianness and data alignment automatically with data-driven or macro approach. In my experience that is needed very rarely. Make char* marshalling work and leave nasty data structures to JSON.

I can hack some more code to demonstrate the idea in more detail if you are interested.