插件技术应用场景:主程序固定,由主开发者维护。插件开放技术标准,由爱好者共同维护、添加。
例如:将开发中的语音助手的插件部份单独开放出来,供以后不断添加功能。
插件程序定义:
Init中定义正则关键词,用于匹配语音中的关键词,以及与之匹配的插件功能函数。
接下来抄一下网上关于官方插件技术的应用。1.8以上支持plugin。
网上还有一个Pingo库是可以简单实现挂件的,但2015年最后更新,且官方带的原本也不复杂,也不就暂时不考虑用它了。
//testplugin.go
package main
import (
"fmt"
)
func init() {
fmt.Println("world")
}
func Hello() {
fmt.Println("hello")
}
编译有所不同:go build -buildmode=plugin testplugin.go,将编译出so文件用于调用。
//main.go
package main
import (
"plugin"
)
func main() {
p, err := plugin.Open("testplugin.so")
if err != nil {
panic(err)
}
f, err := p.Lookup("Hello")
if err != nil {
panic(err)
}
f.(func())()
}
再看看参数调用:
func Hello(name string){
fmt.Println("Hello ",name)
}
#在调用中修改为:
f.(func(string))("ease")
当然以上例子还是太简单,需要增加点实际应用。
插件中调用了一个外部变量
package main
import (
"fmt"
)
var V int
func init(){
fmt.Println("Plugin Init1")
}
func Hello(){
fmt.Println("Hello ",V)
}
调用时就变成了
p1, err := plugin.Open("plugin1.so")
if err != nil {
panic(err)
}
f1, err := p1.Lookup("Hello")
if err != nil {
panic(err)
}
v, err := p1.Lookup("V")
if err != nil {
panic(err)
}
*v.(*int) = 7
f1.(func())()
当然还是不够的,我们使用一些复杂的数据类型
在data目录下添加data.go,用于定义结构体。
如果在各个文件中(插件文件和调用文件)分别定义相同的结构体,会出现错误:
panic: interface conversion: plugin.Symbol is *main.VS, not *main.VS (types from different scopes) 来自不同作用域的类型
type VS struct {
Name string
Age int
School string
}
package main
import (
"fmt"
"./data"
)
var V int
func F() { fmt.Printf("Hello, number %d\n", V) }
var Vs data.VS
func ComplexType() {
fmt.Println(Vs.Name, Vs.Age, Vs.School)
}
package main
import (
"plugin"
"./data"
)
func main() {
p2, err := plugin.Open("plugin2.so")
CheckErr(err)
f2, err := p2.Lookup("Hello")
CheckErr(err)
vs, err := p2.Lookup("Vs")
CheckErr(err)
*vs.(*data.VS) = data.VS{
Name: "Ease",
Age: 42,
School: "know",
}
f2.(func())()
}
学习了网上的例子,换我的思路了,还是为了实现文章开头的目标:语音功能及实现采用插件方式。
-
程序自动搜索.so扩展名的文件
-
所有插件文件在init中必须定义好关键词,以及对应用的功能函数名
-
当语音反馈文字时,分别调用插件关键词定义,看是否匹配关键词。当匹配时调用对应函数完成功能。
根据使用中发现,确实做出来的so文件比较大,比生成单一的可执行文件大。即使加了-ldflags “-s -w"也一样比较大。这是一个暂时不大的问题。