golang实现Go与Lua语言绑定交互的插件库bindergopher-lua的使用

golang实现Go与Lua语言绑定交互的插件库bindergopher-lua的使用

Binder

Binder是一个高级的Go到Lua绑定库。基于yuin/gopher-lua实现。

杀手锏特性

你可以显示详细的错误信息,效果如下:

Error

安装

$ go get -u github.com/alexeyco/binder

运行单元测试:

$ cd $GOPATH/src/github.com/alexeyco/binder
$ go test -cover

示例

函数绑定

package main

import (
	"errors"
	"log"

	"github.com/alexeyco/binder"
)

func main() {
	b := binder.New(binder.Options{
		SkipOpenLibs: true,
	})

	// 绑定log函数到Lua
	b.Func("log", func(c *binder.Context) error {
		t := c.Top()
		if t == 0 {
			return errors.New("need arguments")
		}

		l := []interface{}{}

		for i := 1; i <= t; i++ {
			l = append(l, c.Arg(i).Any())
		}

		log.Println(l...)
		return nil
	})

	// 执行Lua代码
	if err := b.DoString(`
		log('This', 'is', 'Lua')
	`); err != nil {
		log.Fatalln(err)
	}
}

模块绑定

package main

import (
	"errors"
	"log"

	"github.com/alexeyco/binder"
)

func main() {
	b := binder.New()

	// 创建reverse模块
	m := b.Module("reverse")
	m.Func("string", func(c *binder.Context) error {
		if c.Top() == 0 {
			return errors.New("need arguments")
		}

		s := c.Arg(1).String()

		// 反转字符串
		runes := []rune(s)
		for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
			runes[i], runes[j] = runes[j], runes[i]
		}

		c.Push().String(string(runes))
		return nil
	})

	// 执行Lua代码
	if err := b.DoString(`
		local r = require('reverse')
		print(r.string('ABCDEFGHIJKLMNOPQRSTUFVWXYZ'))
	`); err != nil {
		log.Fatalln(err)
	}
}

表绑定

package main

import (
	"errors"
	"log"

	"github.com/alexeyco/binder"
)

type Person struct {
	Name string
}

func main() {
	b := binder.New()

	// 创建person表
	t := b.Table("person")
	t.Static("new", func(c *binder.Context) error {
		if c.Top() == 0 {
			return errors.New("need arguments")
		}
		n := c.Arg(1).String()

		c.Push().Data(&Person{n}, "person")
		return nil
	})

	t.Dynamic("name", func(c *binder.Context) error {
		p, ok := c.Arg(1).Data().(*Person)
		if !ok {
			return errors.New("person expected")
		}

		if c.Top() == 1 {
			c.Push().String(p.Name)
		} else {
			p.Name = c.Arg(2).String()
		}

		return nil
	})

	// 执行Lua代码
	if err := b.DoString(`
		local p = person.new('Steeve')
		print(p:name())

		p:name('Alice')
		print(p:name())
	`); err != nil {
		log.Fatalln(err)
	}
}

选项配置

// Options binder选项对象
type Options struct {
	// CallStackSize 调用栈大小
	CallStackSize int
	// RegistrySize 数据栈大小
	RegistrySize int
	// SkipOpenLibs 控制是否默认打开库
	SkipOpenLibs bool
	// IncludeGoStackTrace 控制当panic发生时是否在Lua堆栈跟踪中包含Go堆栈跟踪
	IncludeGoStackTrace bool
}

使用示例:

b := binder.New(binder.Options{
	SkipOpenLibs: true,
})

错误处理

package main

import (
	"errors"
	"log"
	"os"

	"github.com/alexeyco/binder"
)

type Person struct {
	Name string
}

func main() {
	b := binder.New()
	
	// ...其他代码...

	if err := b.DoString(`-- some string`); err != nil {
		switch err.(type) {
		case *binder.Error:
			e := err.(*binder.Error)
			e.Print() // 打印详细错误信息

			os.Exit(0)
			break
		default:
			log.Fatalln(err)
		}
	}
}

许可证

MIT License

Copyright (c) 2017 Alexey Popov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

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

1 回复

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


使用gopher-lua实现Go与Lua绑定交互

gopher-lua是一个纯Go实现的Lua 5.1虚拟机和编译器,它允许在Go程序中嵌入Lua脚本并实现双向交互。下面我将详细介绍如何使用gopher-lua库。

基本安装

首先安装gopher-lua库:

go get github.com/yuin/gopher-lua

基础用法示例

1. 执行简单Lua脚本

package main

import (
	"github.com/yuin/gopher-lua"
)

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

	// 执行Lua代码
	if err := L.DoString(`print("Hello from Lua!")`); err != nil {
		panic(err)
	}
}

2. Go调用Lua函数

package main

import (
	"fmt"
	"github.com/yuin/gopher-lua"
)

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

	// 加载并执行Lua脚本
	if err := L.DoString(`
		function add(a, b)
			return a + b
		end
	`); err != nil {
		panic(err)
	}

	// 调用Lua函数
	if err := L.CallByParam(lua.P{
		Fn:      L.GetGlobal("add"), // 获取函数
		NRet:    1,                  // 指定返回值数量
		Protect: true,               // 如果为true,则在发生错误时返回错误
	}, lua.LNumber(10), lua.LNumber(20)); err != nil {
		panic(err)
	}

	// 获取返回值
	ret := L.Get(-1)
	L.Pop(1)

	fmt.Printf("Result: %v\n", ret)
}

3. Lua调用Go函数

package main

import (
	"fmt"
	"github.com/yuin/gopher-lua"
)

// 将被Lua调用的Go函数
func add(L *lua.LState) int {
	a := L.ToInt(1) // 获取第一个参数
	b := L.ToInt(2) // 获取第二个参数
	
	L.Push(lua.LNumber(a + b)) // 压入返回值
	return 1                    // 返回结果数量
}

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

	// 注册Go函数到Lua环境
	L.SetGlobal("goAdd", L.NewFunction(add))

	// 在Lua中调用Go函数
	if err := L.DoString(`
		result = goAdd(10, 20)
		print("Result from Go:", result)
	`); err != nil {
		panic(err)
	}
}

高级特性

1. 在Go和Lua之间传递复杂数据结构

package main

import (
	"fmt"
	"github.com/yuin/gopher-lua"
)

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

	// 创建一个Lua表
	L.Push(lua.LString("name"))
	L.Push(lua.LString("Alice"))
	L.SetTable(L.GetGlobal("_G"), lua.LString("person"))

	L.Push(lua.LString("age"))
	L.Push(lua.LNumber(30))
	L.SetTable(L.GetGlobal("_G"), lua.LString("person"))

	// 在Lua中使用这个表
	if err := L.DoString(`
		print("Name:", person.name)
		print("Age:", person.age)
	`); err != nil {
		panic(err)
	}
}

2. 加载外部Lua文件

package main

import (
	"github.com/yuin/gopher-lua"
)

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

	// 加载并执行外部Lua文件
	if err := L.DoFile("script.lua"); err != nil {
		panic(err)
	}
}

3. 错误处理

package main

import (
	"fmt"
	"github.com/yuin/gopher-lua"
)

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

	// 设置错误处理函数
	L.SetMx(1000) // 设置最大栈大小
	L.SetPanicFunction(func(L *lua.LState) {
		fmt.Println("Lua panic:", L.ToString(-1))
	})

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

性能考虑

  1. 重用Lua状态机:创建Lua状态机开销较大,应尽量重用
  2. 预编译常用脚本:使用L.LoadString()预编译脚本,然后多次执行
  3. 减少Go-Lua边界调用:批量处理数据而不是频繁跨语言调用

实际应用场景

  1. 游戏逻辑脚本化
  2. 动态配置系统
  3. 插件系统实现
  4. 业务规则引擎

gopher-lua提供了强大的Go与Lua交互能力,使得在Go应用中嵌入脚本逻辑变得非常简单。通过合理设计,可以充分发挥两种语言的优势。

回到顶部