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);  

—|—

  1. 根据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;  
}  

—|—

  1. 将wrap函数封送到C++

    1
    2

    IntPtr fn = Marshal.GetFunctionPointerForDelegate((LuaCSFunction)   (CSFuncWrap));  
CppDll.RegisterCSFunc(L, "CSFunc", fn);  

—|—

  1. RegisterCSFunc定义如下,将封送的函数以lua_CFunction的方式push到lua

    1
    2
    3
    4
    5

    DLL_API void (lua_State *L, const char *name, lua_CFunction fn)  
{  
	lua_pushcfunction(L, fn);  
	lua_setglobal(L, name);  
}  

—|—

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

  1. 在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  

—|—

  1. 将待调用的lua函数放到栈顶(也是全局函数,如果是表中的函数需要先取一次表)

    1

    CppDll.LuaGetGlobal(L, "LuaFunc");  

—|—

  1. 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

糖果

糖果
LUA教程

Lapis框架的常用处理方法

Lapis框架的常用处理方法 Continue reading

MoonScript实现选择排序

Published on February 26, 2017

MoonScript与Redis客户端

Published on January 19, 2017