0%

在Go中运行Lua脚本(二)

golua

go和c混合编译的库,通过cgo调用c接口

简介

golua 是一个 Go 语言的库,用于在 Go 程序中嵌入 Lua 脚本引擎。它基于 Lua 的 C API,并通过 cgo 将其与 Go 进行绑定,允许开发者在 Go 应用中调用 Lua 脚本,或者让 Lua 脚本调用 Go 函数。

优缺点(来自deepseek的评价)

本来先让通义评价的,但纯TM乱说,还是deepseek靠谱点

优点

  1. 轻量级:Lua 本身是一个轻量级脚本语言,适合嵌入到 Go 应用中。
  2. 高性能:Lua 的执行速度较快,适合作为扩展脚本语言。
  3. 灵活性:允许动态加载和执行脚本,适合插件系统或配置逻辑。
  4. 社区支持:Lua 有丰富的库和文档,便于开发。

缺点

  1. 依赖 CGO:由于是基于 Lua 的 C API,需要 CGO 支持,可能增加构建复杂性。
  2. 内存管理:Lua 和 Go 的垃圾回收机制不同,可能导致内存管理问题。
  3. 性能开销:跨语言调用(Go ↔ Lua)会有一定的性能损失。

实际使用中的一些优缺点

优点

  1. 适合熟悉clua的人玩,不用再去看一遍lua底层代码了
  2. 直接链接的c库,lua51、52、53、54、jit都能玩
  3. 还原lua原始的堆栈操作

缺点

  1. 直接调用c其实很危险,特别是搞扩展c和go交互时,一不小心就崩
  2. 没法搞预编译,针对多虚拟机的优化应该只有使用对象池了

使用技巧

  1. 执行lua文件
    1
    2
    state := slf.LoadFile(file)
    err := slf.Call(slf.GetTop()-1, lua.LUA_MULTRET)
  2. 将go对象注册进lua,并通过lua元表实现go的继承关系
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // 向lua虚拟机注册metatable,并指定父类
    func (slf *Lua) RegistMetaTableWithParent(
    name string,
    funcs map[string]lua.LuaGoFunction,
    parent string,
    ) bool {
    created := slf.NewMetaTable(MetaKey + name) // 入 1
    if !created { // 校验
    slf.Pop(1) // 出 0
    return false
    }
    slf.PushString("__index") // 入 2
    slf.CreateTable(0, len(funcs)) // 入 3
    for fname, f := range funcs {
    slf.PushGoFunction(f) // 入 4
    slf.SetField(-2, fname) // 出 3
    }
    slf.LGetMetaTable(MetaKey + parent) // 入 4
    found := slf.IsTable(-1) // 校验
    if !found {
    slf.Pop(1) // 出 3
    } else {
    slf.SetMetaTable(-2) // 出 3
    }
    slf.SetTable(-3) // 出 1
    slf.Pop(1) // 出 0
    return true
    }

    // 将go对象指针以userdata的形式入栈,并设置其metatable
    func (slf *Lua) PushGoPointerWithMetaTable(ptr interface{}, metatable string) bool {
    value := reflect.ValueOf(ptr)
    if value.Kind() != reflect.Ptr {
    return false
    }
    ptrVal := unsafe.Pointer(value.Pointer())
    rawptr := slf.NewUserdata(unsafe.Sizeof(ptrVal)) // 入 1
    *(*unsafe.Pointer)(rawptr) = unsafe.Pointer(ptrVal)

    // 查找元表
    slf.LGetMetaTable(MetaKey + metatable) // 入 2
    found := slf.IsTable(-1) // 校验
    if !found {
    slf.Pop(1) // 出 1
    } else {
    slf.SetMetaTable(-2) // 出 1
    }
    return true
    }

    // 将go对象指针出栈
    func (slf *Lua) ToGoPointer(pos int) unsafe.Pointer {
    return *(*unsafe.Pointer)(slf.ToUserdata(pos))
    }