英文:
golua - declaring lua class with defined methods
问题
我正在尝试使用golua包为我的Go应用程序构建扩展API。我的想法是将几个类暴露给Lua虚拟机,例如Book
类:
local book = Book.Create("Le Petit Prince")
print(book)
book:save()
目前我只能做到基本的:
type Book struct {
Id int64
Title string
}
func BookCreate(L *lua.State) int {
title := L.ToString(1)
p := &Book{Id: 1, Title: title}
L.PushGoStruct(p)
return 1
}
func BookToString(L *lua.State) int {
book, _ := L.ToGoStruct(1).(*Book)
L.PushString(fmt.Sprintf("Book(Id=%d, Title=\"%s\")", book.Id, book.Title))
return 1
}
L := lua.NewState()
defer L.Close()
L.OpenLibs()
L.NewMetaTable("Book")
L.SetMetaMethod("Create", BookCreate)
L.SetMetaMethod("tostring", BookToString)
L.SetGlobal("Book")
这样我就可以做到这一点:
local book = Book.Create("Le Petit Prince")
print(Book.tostring(book))
但无法做到这一点:
local book = Book.Create("Le Petit Prince")
print(book:tostring())
// reflect: call of reflect.Value.Type on zero Value
我的问题
- 如何创建与A Simplified Way to Declare Lua Classes中描述的类等效的Lua类?
- 如何为类添加类似
__tostring
这里的"魔术方法"?
英文:
I'm trying to use golua package to build an extension API for my application written in Go. My idea is to have several classes exposed to lua VM, for example Book
class:
<!-- language: lua -->
local book = Book.Create("Le Petit Prince")
print(book)
book:save()
What I'm able to do now is just basic:
<!-- language: golang -->
type Book struct {
Id int64
Title string
}
func BookCreate(L *lua.State) int {
title := L.ToString(1)
p := &Book{Id: 1, Title: title}
L.PushGoStruct(p)
return 1
}
func BookToString(L *lua.State) int {
book, _ := L.ToGoStruct(1).(*Book)
L.PushString(fmt.Sprintf("Book(Id=%d, Title=\"%s\")", book.Id, book.Title))
return 1
}
L := lua.NewState()
defer L.Close()
L.OpenLibs()
L.NewMetaTable("Book")
L.SetMetaMethod("Create", BookCreate)
L.SetMetaMethod("tostring", BookToString)
L.SetGlobal("Book")
Which allows me to do this:
<!-- language: lua -->
local book = Book.Create("Le Petit Prince")
print(Book.tostring(book))
But not this:
local book = Book.Create("Le Petit Prince")
print(book:tostring())
// reflect: call of reflect.Value.Type on zero Value
My questions
- How to create lua class equivalent to one described in A Simplified Way to Declare Lua Classes?
- How to add "magic methods" to class like
__tostring
here
答案1
得分: 1
我没有使用过Go语言,但是看起来你没有将Book
设置为你创建的新书的元表。我相当确定这不会自动发生。
在这里我找到了一个例子:https://github.com/stevedonovan/luar/blob/master/luar.go#L52
重要的是,当一个用户数据或任何对象被创建时(比如你的书),你需要获取全局元表,然后使用L.SetMetaTable(-2)
将其设置为元表。
英文:
I have not used go, but it looks like you never set Book
as a metatable for the new book you create. Im fairly sure this does not happen automatically.
See an example I found here https://github.com/stevedonovan/luar/blob/master/luar.go#L52
What is important here is that when a userdata or any object is made (your book), you need to get the global metatable and then set it as metatable with L.SetMetaTable(-2)
答案2
得分: 0
在@Rochet2的回答和阅读《Programming in Lua》的第28章:C中的用户定义类型之后,我想出了一个可行的解决方案。我对Lua一无所知,对Go也只有一点了解,所以我的结论可能是错误的。
BookCreate
函数
我们创建了一个新的userdata,而不是将普通的Go struct推入堆栈。然后,我们将新创建的book
的metatable设置为"Book"。
L.NewUserdata
返回unsafe.Pointer
,所以我们将其转换为Book
。
func BookCreate(L *lua.State) int {
title := L.ToString(1)
book := (*Book)(L.NewUserdata(uintptr(unsafe.Sizeof(Book{}))))
L.LGetMetaTable("Book")
L.SetMetaTable(-2)
book.Id = 1;
book.Title = title;
return 1
}
BookToString
函数
在这里,我们只是从堆栈中取出userdata并将其转换为Book
。
请注意,我们从堆栈中弹出的内容可能不是指向
Book
结构体的指针,所以如果我们想要防止空指针/其他Go错误,可能需要进行一些基本的类型检查。
func BookToString(L *lua.State) int {
book := (*Book)(L.ToUserdata(1))
L.PushString(fmt.Sprintf("Book(Id=%d, Title=\"%s\")", book.Id, book.Title))
return 1
}
main
函数
我们初始化了一个名为"Book"的元表,并使其成为自己的元表。我们添加了两个方法Create
和__tostring
,并将"Book"设置为全局变量。
L := lua.NewState()
defer L.Close()
L.OpenLibs()
L.NewMetaTable("Book")
L.PushString("__index")
L.PushValue(-2)
L.SetTable(-3)
L.SetMetaMethod("Create", BookCreate)
L.SetMetaMethod("__tostring", BookToString)
L.SetGlobal("Book")
现在我们可以在Lua中进行以下操作:
local book = Book.Create('Le Petit Prince')
print(book)
print(book:__tostring())
print(Book.__tostring(book))
--输出:
Book(Id=1, Title="Le Petit Prince")
Book(Id=1, Title="Le Petit Prince")
Book(Id=1, Title="Le Petit Prince")
希望有人会觉得这个有用。完整代码在这里。
英文:
After @Rochet2's answer and after reading Chapter 28. User-Defined Types in C of Programming in Lua I've came up with working solution. I know nothing about Lua and just a little about Go so I might be wrong in my conclusions.
> This edition of Programming in Lua was written for Lua 5.0 - golua utilizes Lua 5.1 (for now).
BookCreate
function
We create new userdata instead of pushing plain Go struct to stack. Then we set metatable of newly created book
to "Book".
L.NewUserdata
returns unsafe.Pointer
so we cast it to Book
.
<!-- language: golang -->
func BookCreate(L *lua.State) int {
title := L.ToString(1)
book := (*Book)(L.NewUserdata(uintptr(unsafe.Sizeof(Book{}))))
L.LGetMetaTable("Book")
L.SetMetaTable(-2)
book.Id = 1;
book.Title = title;
return 1
}
BookToString
function
Here we just take userdata from stack and cast it to Book
.
> Be aware that what we pop out of stack might not be pointer to Book
struct so if we want to prevent nil pointers / other Go errors some basic type check might be required.
func BookToString(L *lua.State) int {
book := (*Book)(L.ToUserdata(1))
L.PushString(fmt.Sprintf("Book(Id=%d, Title=\"%s\")", book.Id, book.Title))
return 1
}
main
function
We initialize new "Book" metatable and make itself its own metatble. We add two methods Create
and __tostring
and make "Book" a global.
L := lua.NewState()
defer L.Close()
L.OpenLibs()
L.NewMetaTable("Book")
L.PushString("__index")
L.PushValue(-2)
L.SetTable(-3)
L.SetMetaMethod("Create", BookCreate)
L.SetMetaMethod("__tostring", BookToString)
L.SetGlobal("Book")
This is what we can do in Lua now:
local book = Book.Create('Le Petit Prince')
print(book)
print(book:__tostring())
print(Book.__tostring(book))
--out:
Book(Id=1, Title="Le Petit Prince")
Book(Id=1, Title="Le Petit Prince")
Book(Id=1, Title="Le Petit Prince")
I hope someone will find it useful. Full code here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论