Lua-为什么C函数作为用户数据返回? [英] Lua - Why are C functions returned as userdata?

查看:106
本文介绍了Lua-为什么C函数作为用户数据返回?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为引擎制作游戏脚本,并且正在使用一个元表将函数从表(存储玩家的自定义函数和数据)中的功能重定向到userdata对象(这是我的Player类的主要实现).用户可以使用self来引用两者.

I'm working on game scripting for my engine and am using a metatable to redirect functions from a table (which stores custom functions and data for players) to a userdata object (which is the main implementation for my Player class) so that users may use self to refer to both.

这是我在Player类中的C#中进行绑定的方式:

This is how I do my binding in C# in the Player class:

        state.NewTable("Player");    // Create Player wrapper table
        state["Player.data"] = this; // Bind Player.data to the Player class
        state.NewTable("mt");        // Create temp table for metatable
        state.DoString(@"mt.__index = function(self,key)
                             local k = self.data[key]
                             if key == 'data' or not k then
                                 return rawget(self, key)
                             elseif type(k) ~= 'function' then
                                 print(type(k))
                                 print(k)
                                 return k
                             else
                                 return function(...)
                                     if self == ... then
                                         return k(self.data, select(2,...))
                                     else
                                         return k(...)
                                     end
                                 end
                             end
                         end");
        state.DoString("setmetatable(Player, mt)"); // Change Player's metatable

对于我的Player类,我实现了一个方法bool IsCommandActive(string name).当我需要使用self调用此方法时,它需要使用userdata对象而不是表,否则会出现以下错误:

For my Player class, I implement a method, bool IsCommandActive(string name). When I need to call this method using self, it needs to use the userdata object, rather than the table, otherwise I get the following error:

NLua.Exceptions.LuaScriptException:'实例方法'IsCommandActive' 需要一个非空的目标对象"

NLua.Exceptions.LuaScriptException: 'instance method 'IsCommandActive' requires a non null target object'

出于明显的原因.这是因为self引用表,而不是userdata.因此,我实现了一个元表,以便它可以使用self来引用任何一个.该实现取自此处,但这是我的特定变体(我的用户数据存储在名为data的索引中:

For obvious reasons. This is because self refers to the table, not the userdata. So I implemented a metatable so that it may use self to refer to either. The implementation is taken from here, but here is my particular variant (my userdata is stored in an index called data:

mt.__index = function(self,key)
    local k = self.data[key]
        if key == 'data' or not k then
            return rawget(self, key)
        elseif type(k) ~= 'function' then
            print(type(k))
            print(k)
            return k
        else
            return function(...)
                if self == ... then
                    return k(self.data, select(2,...))
                else
                    return k(...)
                end
            end
        end
    end
end

很明显,我遵循的是setmetatable.

现在我要问的问题了.请注意如何在elseif下打印type(k)print(k).这是因为我注意到我仍然遇到相同的错误,所以我想进行一些调试.这样做时,我得到以下输出(我相信这是对IsCommandActive的输出):

Now to the meat of my question. Notice how I print type(k) and print(k) under the elseif. This is because I noticed that I was still getting the same error, so I wanted to do some debugging. When doing so, I got the following output (which I believe is for IsCommandActive):

userdata: 0BD47190

不是要打印'function'吗?为什么打印'userdata: 0BD47190'?最后,如果确实如此,如何确定该值是否为C函数,以便进行适当的重定向?

Shouldn't it be printing 'function'? Why is it printing 'userdata: 0BD47190'? Finally, if that is indeed the case, how can I detect if the value is a C function so I may do the proper redirection?

推荐答案

在大量阅读了有关元表的知识之后,我设法解决了我的问题.

After lots of reading about metatables, I managed to solve my problem.

要回答标题中的问题,很显然,NLua决定做什么,并且具体取决于实现.在任何其他绑定中,它很可能返回为function,但是对于NLua显然不是这种情况.

To answer the question in the title, it's apparently what NLua just decides to do and is implementation-specific. In any other bindings, it may very well return as function, but such is apparently not the case for NLua.

关于我如何完成自己想要的事情,我必须定义元表__index__newindex函数:

As for how I managed to accomplish what I wanted, I had to define the metatable __index and __newindex functions:

        state.NewTable("Player");
        state["Player.data"] = this;
        state.NewTable("mt");
        state.DoString(@"mt.__index = function(self,key)
                             local k = self.data[key]
                             local metatable = getmetatable(k)
                             if key == 'data' or not k then
                                 return rawget(self, key)
                             elseif type(k) ~= 'function' and (metatable == nil or metatable.__call == nil) then
                                 return k
                             else
                                 return function(...)
                                     if self == ... then
                                         return k(self.data, select(2,...))
                                     else
                                         return k(...)
                                     end
                                 end
                             end
                         end");
        state.DoString(@"mt.__newindex = function(self, key, value)
                             local c = rawget(self, key, value)
                             if not c then
                                 local dataHasKey = self.data[key] ~= key
                                 if not dataHasKey then
                                     rawset(self, key, value)
                                 else
                                     self.data[key] = value
                                 end
                             else
                                 rawset(self, key, value)
                             end
                         end");
        state.DoString("setmetatable(Player, mt)");

__index的作用是重写表的索引方式.在此实现中,如果在Player包装器表中未找到 ,则它会尝试从Player.data中的userdata检索它.如果那里不存在,那么Lua会做它的事情并返回nil.

What __index does is override how tables are indexed. In this implementation, if key is not found in the Player wrapper table, then it goes and tries to retrieve it from the userdata in Player.data. If it doesn't exist there, then Lua just does its thing and returns nil.

就这样,我可以从userdata中检索字段!但是,我很快开始注意到,如果我在Lua中设置了self.Pos,则Player.Pos不会不会在支持C#代码中进行更新.很快,我意识到这是因为PosPlayer包装器表中生成未命中,这意味着它正在为表创建一个新的Pos字段,因为它实际上不存在!

And just like that, I could retrieve fields from the userdata! I quickly began to notice, however, that if I set, for instance, self.Pos in Lua, then the Player.Pos would not update in the backing C# code. Just as quickly, I realized that this was because Pos was generating a miss in the Player wrapper table, which meant that it was creating a new Pos field for the table since it actually did not exist!

这不是预期的行为,因此我也必须覆盖__newindex.在此特定实现中,它检查Player.data(userdata)是否具有key,如果是,则为该特定key设置数据.如果userdata中不存在它,则应该为Player包装器表创建它,因为它应该是用户自定义Player实现的一部分.

This was not the intended behavior, so I had to override __newindex as well. In this particular implementation, it checks if the Player.data (userdata) has the key, and if so, sets the data for that particular key. If it does not exist in the userdata, then it should create it for the Player wrapper table because it should be part of the user's custom Player implementation.

这篇关于Lua-为什么C函数作为用户数据返回?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆