golua – 声明具有定义方法的Lua类

huangapple go评论93阅读模式
英文:

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

我的问题

  1. 如何创建与A Simplified Way to Declare Lua Classes中描述的类等效的Lua类?
  2. 如何为类添加类似__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(&quot;Le Petit Prince&quot;)
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 := &amp;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(&quot;Book(Id=%d, Title=\&quot;%s\&quot;)&quot;, book.Id, book.Title))
    return 1
}

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

L.NewMetaTable(&quot;Book&quot;)
L.SetMetaMethod(&quot;Create&quot;, BookCreate)
L.SetMetaMethod(&quot;tostring&quot;, BookToString)
L.SetGlobal(&quot;Book&quot;)

Which allows me to do this:

<!-- language: lua -->

local book = Book.Create(&quot;Le Petit Prince&quot;)
print(Book.tostring(book))

But not this:

local book = Book.Create(&quot;Le Petit Prince&quot;)
print(book:tostring())

// reflect: call of reflect.Value.Type on zero Value

My questions

  1. How to create lua class equivalent to one described in A Simplified Way to Declare Lua Classes?
  2. 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推入堆栈。然后,我们将新创建的bookmetatable设置为"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(&quot;Book&quot;)
    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(&quot;Book(Id=%d, Title=\&quot;%s\&quot;)&quot;, 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(&quot;Book&quot;)
L.PushString(&quot;__index&quot;)
L.PushValue(-2)
L.SetTable(-3)
L.SetMetaMethod(&quot;Create&quot;, BookCreate)
L.SetMetaMethod(&quot;__tostring&quot;, BookToString)
L.SetGlobal(&quot;Book&quot;)

This is what we can do in Lua now:

local book = Book.Create(&#39;Le Petit Prince&#39;)
print(book)
print(book:__tostring())
print(Book.__tostring(book))

--out:
Book(Id=1, Title=&quot;Le Petit Prince&quot;)
Book(Id=1, Title=&quot;Le Petit Prince&quot;)
Book(Id=1, Title=&quot;Le Petit Prince&quot;)

I hope someone will find it useful. Full code here.

huangapple
  • 本文由 发表于 2016年1月18日 02:07:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/34841773.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定