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

Golang实现Go与Lua语言绑定交互的插件库binder使用指南

简介

Binder是一个高级的Go与Lua绑定库,基于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("需要参数")
		}

		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("需要参数")
		}

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

	// 使用reverse模块
	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("需要参数")
		}
		n := c.Arg(1).String()  // 获取名字参数

		// 创建Person对象并推入栈
		c.Push().Data(&Person{n}, "person")
		return nil
	})

	// 动态绑定name方法
	t.Dynamic("name", func(c *binder.Context) error {
		// 获取Person对象
		p, ok := c.Arg(1).Data().(*Person)
		if !ok {
			return errors.New("需要person对象")
		}

		// 根据参数数量决定是获取还是设置name
		if c.Top() == 1 {
			c.Push().String(p.Name)  // 获取名字
		} else {
			p.Name = c.Arg(2).String()  // 设置名字
		}

		return nil
	})

	// 使用person表
	if err := b.DoString(`
		local p = person.new('Steeve')
		print(p:name())

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

选项配置

// Options 绑定器选项对象
type Options struct {
	// CallStackSize 调用栈大小
	CallStackSize int
	// RegistrySize 数据栈大小
	RegistrySize int
	// SkipOpenLibs 控制是否默认加载库
	SkipOpenLibs bool
	// IncludeGoStackTrace 是否在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)
		}
	}
}

注意事项

如果设置SkipOpenLibstrue,以下基础库仍会被加载:

  • 所有基本函数
  • table库
  • package库

许可证

MIT许可证


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

1 回复

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


Go与Lua语言绑定交互的插件库binder使用指南

binder是一个用于Go与Lua语言绑定的库,它允许你在Go程序中嵌入Lua脚本,并实现两者之间的高效交互。下面我将详细介绍如何使用binder库。

安装binder

首先安装binder库:

go get github.com/alexeyco/binder

基本使用

1. 创建Lua虚拟机并绑定Go函数

package main

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

func main() {
	// 创建Lua虚拟机
	l := lua.NewState()
	defer l.Close()

	// 创建binder实例
	b := binder.New(binder.Options{
		SkipOpenLibs: false, // 是否跳过标准库加载
	})

	// 将binder绑定到Lua虚拟机
	b.Bind(l)

	// 定义一个Go函数并注册到Lua
	b.Func("greet", func(c *binder.Context) error {
		if c.Top() == 0 {
			return c.ArgError(1, "expected name")
		}
		name := c.Arg(1).String()
		c.Push().String(fmt.Sprintf("Hello, %s!", name))
		return nil
	})

	// 执行Lua脚本
	err := l.DoString(`
		print(greet("World"))
	`)
	if err != nil {
		panic(err)
	}
}

2. 绑定Go结构体到Lua

package main

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

type Person struct {
	Name string
	Age  int
}

func (p *Person) Greet() string {
	return fmt.Sprintf("I'm %s, %d years old", p.Name, p.Age)
}

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

	b := binder.New(binder.Options{})
	b.Bind(l)

	// 注册Person类型到Lua
	b.Struct("person", &Person{}, func(p *Person) binder.FuncMap {
		return binder.FuncMap{
			"greet": func(c *binder.Context) error {
				c.Push().String(p.Greet())
				return nil
			},
			"set_age": func(c *binder.Context) error {
				if c.Top() == 0 {
					return c.ArgError(1, "expected age")
				}
				p.Age = c.Arg(1).Int()
				return nil
			},
		}
	})

	// 执行Lua脚本
	err := l.DoString(`
		local p = person.new()
		p.name = "Alice"
		p:set_age(30)
		print(p:greet())
	`)
	if err != nil {
		panic(err)
	}
}

高级特性

1. 错误处理

b.Func("divide", func(c *binder.Context) error {
	if c.Top() < 2 {
		return c.ArgError(1, "expected two arguments")
	}
	a := c.Arg(1).Float64()
	b := c.Arg(2).Float64()
	
	if b == 0 {
		return c.Error("division by zero")
	}
	
	c.Push().Number(a / b)
	return nil
})

2. 表操作

b.Func("create_table", func(c *binder.Context) error {
	t := c.NewTable()
	t.RawSetString("name", lua.LString("Table"))
	t.RawSetString("value", lua.LNumber(42))
	c.Push().Table(t)
	return nil
})

3. 从Go调用Lua函数

// 在Lua中定义函数
err := l.DoString(`
	function add(a, b)
		return a + b
	end
`)
if err != nil {
	panic(err)
}

// 从Go调用Lua函数
fn := l.GetGlobal("add").(*lua.LFunction)
err = l.CallByParam(lua.P{
	Fn:      fn,
	NRet:    1,
	Protect: true,
}, lua.LNumber(10), lua.LNumber(20))
if err != nil {
	panic(err)
}

ret := l.ToNumber(-1)
l.Pop(1)
fmt.Println("Result:", ret)

性能优化建议

  1. 复用Lua虚拟机:避免频繁创建和销毁Lua虚拟机
  2. 预编译脚本:对于频繁使用的脚本,可以先编译后多次执行
  3. 减少Go/Lua边界调用:尽量减少两种语言间的函数调用
  4. 使用池化技术:对于频繁创建的对象考虑使用对象池

实际应用场景

  1. 游戏脚本系统
  2. 动态配置解析
  3. 业务规则引擎
  4. 插件系统实现
  5. 自动化测试脚本

binder库提供了Go与Lua之间简洁高效的交互方式,通过合理使用可以极大增强应用程序的灵活性和扩展性。

回到顶部