Lua和C#交互

项目中一直使用tolua框架,底层使用C#开发,逻辑使用lua开发。借助tolua的源码,研究一下lua与C#是如何互相调用的,然后自己写了个简单的demo作为记录。

Demo里没有拿lua的源码编译,而是直接使用lua的静态库,将lua库中的方法封装成为动态库dll给C#使用。涉及到两个工程:

  • 首先是c++的dll工程,包含了lua库的一些方法(此处仅为演示,所以手动包装了一些用到的),暴露给C#调用;

  • 其次是c#的工程,引用前一步的dll库,并且通过其接口来调用lua库的函数以实现交互。

涉及到的源码托管在GitHub

lua-cs

在lua中调用C#基本步骤如下(仅供演示用,其中省略掉了lua栈数据及类型检查等步骤):

  1. 首先定义委托类型LuaCSFunction

    1
    2
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int LuaCSFunction(IntPtr luaState);
  2. 根据C#函数(CSFunc)得到对应的wrap函数(CSFuncWrap

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    static int CSFunc(int a, int b)
    {
    Console.WriteLine("[CS] this is CSFunc with args a = " + a + " and b = " + b);
    int ret = 10 * a + b;
    Console.WriteLine("[CS] ret will be " + ret);
    return ret;
    }

    static int CSFuncWrap(IntPtr L)
    {
    CppDll.LuaGetTop(L);
    int arg0 = (int)CppDll.LuaToNumber(L, 1);
    int arg1 = (int)CppDll.LuaToNumber(L, 2);
    int o = CSFunc(arg0, arg1);
    CppDll.LuaPushNumber(L, o);
    return 1;
    }
  3. 将wrap函数封送到C++

    1
    2
    IntPtr fn = Marshal.GetFunctionPointerForDelegate((LuaCSFunction)   (CSFuncWrap));
    CppDll.RegisterCSFunc(L, "CSFunc", fn);
  4. RegisterCSFunc定义如下,将封送的函数以lua_CFunction的方式push到lua

    1
    2
    3
    4
    5
    DLL_API void RegisterCSFunc(lua_State *L, const char *name, lua_CFunction fn)
    {
    lua_pushcfunction(L, fn);
    lua_setglobal(L, name);
    }

    这里是以全局函数的方式注册的,表函数类似,先取表再push。

  5. 在lua中调用wrap函数

    1
    local ret = CSFunc(1,2);

cs-lua

在C#中调用lua的主要步骤(同样简化省略掉了一些对于lua栈中数据及类型判断的过程):

  1. 在lua中定义函数如下

    1
    2
    3
    4
    5
    6
    LuaFunc = function(x,y) 
    print('[lua] this is LuaFunc with args x = ' .. x .. ' and y = ' .. y);
    local ret = x * 10 + y;
    print('[lua] ret will be ' .. ret);
    return ret;
    end
  2. 将待调用的lua函数放到栈顶(也是全局函数,如果是表中的函数需要先取一次表)

    1
    CppDll.LuaGetGlobal(L, "LuaFunc");
  3. push参数,调用pcall,获取结果

    1
    2
    3
    4
    CppDll.LuaPushNumber(L, 3);
    CppDll.LuaPushNumber(L, 5);
    CppDll.LuaPCall(L, 2, 1, 0);
    int ret = (int)CppDll.LuaToNumber(L, 1);

这一过程可以进一步封装,并在C#中缓存lua函数以减少lua栈操作。

REFERENCE

Lua程序设计(第二版)

https://github.com/topameng/tolua_runtime

https://github.com/topameng/tolua