代码之家  ›  专栏  ›  技术社区  ›  Gdeglin

C没有存根的RPC

rpc c
  •  2
  • Gdeglin  · 技术社区  · 16 年前

    我有一个程序,我希望其他进程能够调用函数(通过Unix套接字)。消息协议非常简单,函数名、函数签名和保存参数的缓冲区(char*)。

    当我程序中的一个模块想要允许一个函数被访问时,它会在库中注册名称和签名。我面临的问题是,一旦请求出现,我就物理地调用函数。我已经查看了RPC和JavaRMI之类的库,但这些都需要我生成存根来封装调用。我正在工作的系统是非常动态的,我还必须与其他人的代码接口,我不能修改。

    所以基本上,一个函数可能看起来像:

    int somefunc(int someparam, double another)
    {
        return 1234;
    }
    

    现在我在图书馆注册:

    //           func ptr   name       signature
    REG_FUNCTION(somefunc, "somefunc", "i:id");
    

    当请求进入时,我会进行一些错误检查,一旦有效,我就想调用函数。所以我有变量:

    void * funcptr = (the requested function);
    char * sig = (the function signature);
    char * params = (a buffer of function parameters);
    //note that the params buffer can hold data types of arbitrary lengths
    

    如何调用参数为C的函数?

    谢谢!

    2 回复  |  直到 13 年前
        1
  •  2
  •   unwind    16 年前

    我不认为这是完全可以解决的,一般来说,只使用C。例如,您不知道目标函数使用的调用约定。有一个风险,你最终会“欺骗”编译器,或至少不得不二次猜测它。如果编译器决定使用寄存器中传递的参数来构建注册函数,可能是由于某些优化设置(或者如果它是用不同的编译器构建的呢?).

    一般来说,也没有办法用C来表示您想要用一组给定参数调用一个函数,并且这些参数的值需要从一个随机字节的缓冲区中解包。

    你可以这样做:

    enum { VOID_INT, VOID_INT_DOUBLE, VOID_DOUBLE_INT, ... } Signature;
    
    void do_call(const void *userfunction, const void *args, Signature sig)
    {
      switch(signature)
      {
        case VOID_INT:
        {
          int x = *(int *) args;
          void (*f)(int) = userfunction;
          f(x);
          break;
        }
        case VOID_INT_DOUBLE:
        ...
      }
    }
    

    但很明显,这与可扩展性不在同一个大陆上。当然,您可以为一些合理的返回类型和参数类型集自动生成此代码。你仍然有点不知所措 void * 但这可能是可以接受的。但是,仍然有人给你一个没有预先生成代码的签名的风险。

        2
  •  0
  •   Sven    13 年前

    退房 libffi . 此库允许使用运行时指定的一组参数调用函数。