golang实现Go与Lua互操作的C语言绑定插件库golua的使用

Golang实现Go与Lua互操作的C语言绑定插件库golua的使用

安装

最简单的安装方式:

go get github.com/aarzilli/golua/lua

安装后可以尝试运行示例:

cd golua/_example/
go run basic.go
go run alloc.go 
go run panic.go
go run userdata.go

构建配置

这个库使用构建标签进行配置。默认情况下它会查找名为:

  • Linux和macOS:lua5.1
  • Windows:lua
  • FreeBSD:lua-5.1

如果不起作用,可以使用:

  • -tags luadash5.1 强制使用 lua-5.1
  • -tags llua 强制使用 lua

如果想静态链接到liblua.a,可以使用 -tags luaa。也可以通过指定 -tags luajit 使用Luajit。

默认使用lua5.1,但也支持:

  • lua5.2:-tags lua52
  • lua5.3:-tags lua53
  • lua5.4:-tags lua54

如果系统上的库名称包含破折号(如liblua-5.4),使用lluadash标签:go build -tags lua54,lluadash ...

快速开始

创建新的虚拟机:

L := lua.NewState()
L.OpenLibs()
defer L.Close()

Lua虚拟机是基于栈的,可以这样调用Lua函数:

// 将"print"函数压入栈
L.GetGlobal("print")
// 将字符串"Hello World!"压入栈
L.PushString("Hello World!")
// 调用print函数,传入1个参数,预期没有返回值
L.Call(1, 0)

更实用的方式是执行Lua代码文件或字符串:

// 执行Lua代码字符串
err := L.DoString("...")
// 执行Lua文件
err = L.DoFile(filename)

注册Go函数到Lua

可以将Go函数发布到虚拟机中:

func adder(L *lua.State) int {
    a := L.ToInteger(1)  // 获取第一个参数
    b := L.ToInteger(2)  // 获取第二个参数
    L.PushInteger(a + b) // 将结果压入栈
    return 1             // 返回值的数量
}

func main() {
    L := lua.NewState()
    defer L.Close()
    L.OpenLibs()

    L.Register("adder", adder)  // 注册adder函数
    L.DoString("print(adder(2, 2))") // 调用adder函数
}

错误处理

Lua的异常与Go不兼容,golua通过以下方式解决:

  1. 通常不能从Lua进行异常处理,pcallxpcall被重命名为unsafe_pcallunsafe_xpcall
  2. 在新创建的lua.State上的方法调用发生在不受保护的环境中

安全初始化的方式:

func LuaStateInit(L *lua.State) int {
    // 初始化代码
    return 0
}

L.PushGoFunction(LuaStateInit)
err := L.Call(0, 0)

线程和协程

lua.State不是线程安全的,但库本身是线程安全的。Lua的协程存在但未经充分测试。

其他注意事项

  • 要构建针对lua5.2、lua5.3或lua5.4,使用构建标签lua52、lua53或lua54
  • 从源代码编译只产生静态链接库(liblua.a)

LuaJIT支持

要链接luajit-2.0.x,可以使用CGO_CFLAGS和CGO_LDFLAGS环境变量:

CGO_CFLAGS=`pkg-config luajit --cflags`
CGO_LDFLAGS=`pkg-config luajit --libs-only-L`
go get -f -u -tags luajit github.com/aarzilli/golua/lua

许可证

GoLua在MIT许可证下发布。Lua的版权归Lua.org, PUC-Rio所有。


更多关于golang实现Go与Lua互操作的C语言绑定插件库golua的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现Go与Lua互操作的C语言绑定插件库golua的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Go与Lua互操作:golua库使用指南

golua是一个Go语言的Lua绑定库,它允许Go程序调用Lua脚本,也允许Lua脚本调用Go函数。下面我将详细介绍如何使用golua实现Go与Lua的互操作。

安装golua

首先需要安装golua库:

go get github.com/stevedonovan/golua

注意:使用前需要确保系统已安装Lua开发库(如Ubuntu上需要安装liblua5.1-dev

基本用法

1. 初始化Lua环境

package main

import (
	"fmt"
	lua "github.com/stevedonovan/golua/lua"
	"os"
)

func main() {
	L := lua.NewState()  // 创建新的Lua状态
	defer L.Close()      // 确保最后关闭

	// 加载并执行Lua脚本
	if err := L.DoFile("script.lua"); err != nil {
		fmt.Println("执行Lua脚本出错:", err)
		os.Exit(1)
	}
}

2. Go调用Lua函数

假设有一个Lua脚本script.lua

function add(a, b)
    return a + b
end

Go代码调用这个Lua函数:

func callLuaAdd(L *lua.State) {
	L.GetGlobal("add")          // 获取全局函数add
	L.PushInteger(10)           // 压入第一个参数
	L.PushInteger(20)           // 压入第二个参数
	L.Call(2, 1)                // 调用函数,2个参数,1个返回值
	
	result := L.ToInteger(-1)   // 获取栈顶的返回值
	L.Pop(1)                    // 弹出返回值
	fmt.Println("10 + 20 =", result)
}

3. Lua调用Go函数

首先在Go中注册函数:

// 定义一个Go函数供Lua调用
func goAdd(L *lua.State) int {
	a := L.ToInteger(1)  // 获取第一个参数
	b := L.ToInteger(2)  // 获取第二个参数
	L.PushInteger(a + b) // 将结果压入栈
	return 1             // 返回结果数量
}

func registerGoFunctions(L *lua.State) {
	L.Register("go_add", goAdd)  // 注册Go函数到Lua环境
}

然后在Lua中调用这个函数:

result = go_add(5, 7)
print("5 + 7 =", result)

高级用法

1. 处理Lua表

func processLuaTable(L *lua.State) {
	L.GetGlobal("person") // 假设person是一个全局表
	
	// 获取表字段
	L.GetField(-1, "name")
	name := L.ToString(-1)
	L.Pop(1)
	
	L.GetField(-1, "age")
	age := L.ToInteger(-1)
	L.Pop(1)
	
	fmt.Printf("Name: %s, Age: %d\n", name, age)
	L.Pop(1) // 弹出person表
}

对应的Lua表定义:

person = {
    name = "Alice",
    age = 30
}

2. 错误处理

func safeCall(L *lua.State) {
	// 设置错误处理函数
	errFunc := L.AtPanic(func(L *lua.State) int {
		err := L.ToString(-1)
		fmt.Println("Lua panic:", err)
		return 0
	})
	defer L.Pop(1) // 弹出错误处理函数

	// 尝试执行可能出错的代码
	if err := L.DoString(`error("something went wrong")`); err != nil {
		fmt.Println("执行出错:", err)
	}
}

完整示例

package main

import (
	"fmt"
	lua "github.com/stevedonovan/golua/lua"
	"os"
)

// Lua调用的Go函数
func multiply(L *lua.State) int {
	a := L.ToInteger(1)
	b := L.ToInteger(2)
	L.PushInteger(a * b)
	return 1
}

func main() {
	L := lua.NewState()
	defer L.Close()

	// 注册Go函数到Lua环境
	L.Register("go_multiply", multiply)

	// 执行Lua代码
	luaCode := `
		function add(a, b)
			return a + b
		end

		local sum = add(10, 20)
		print("Lua计算10+20="..sum)

		local product = go_multiply(5, 6)
		print("Go计算5*6="..product)

		return "success"
	`

	if err := L.DoString(luaCode); err != nil {
		fmt.Println("执行Lua代码出错:", err)
		os.Exit(1)
	}

	// 获取Lua返回值
	if L.GetTop() > 0 {
		ret := L.ToString(-1)
		fmt.Println("Lua脚本返回值:", ret)
		L.Pop(1)
	}
}

注意事项

  1. 内存管理:确保每个NewState()都有对应的Close()
  2. 栈平衡:调用Lua函数后要保持栈平衡
  3. 类型转换:注意Go和Lua类型之间的转换
  4. 错误处理:始终检查Lua调用的错误

golua提供了Go与Lua互操作的基本功能,对于更复杂的需求,可能需要考虑其他更现代的Lua绑定库如gopher-lua或lua-go。

回到顶部