草庐IT

c++ - 将已存在的对象从 C++ 返回到 Lua

coder 2024-02-24 原文

在 C++ 中,假设我有一个创建二叉树结构的类,我使用它是这样的:

CTreeRoot* root = new CTreeRoot(/* whatever */);
CNode* leftNode = root->getLeftNode();
CNode* rightNode = root->getRightNOde();

leftNode->doSomething();
rightNode->doSomething();

// etc

并假设左右节点有自己的左右节点(因此,二叉树)。现在我想将它公开给 Lua(使用 luabind)这样我就可以做同样的事情:

local root = treeroot.new(/* whatever */)
local left = root:getLeftNode()
local right = root:getRightNode()

left:doSomething();
right:doSomething(); 

我已经完成了其中的大部分工作。但是,对于 getLeftNode() 和 getRightNode() 方法,我很确定我做的是“错误的”。以下是我在 C++ 中实现 getLeftNode() 的方式:

int MyLua::TreeRootGetLeftNode(luaState* L)
{
    CTreeRoot* root = (CTreeRoot*)luaL_checkudata(L, 1, "MyLua.treeroot");
    CNode* leftNode = root->getLeftNode();

    if (leftNode != NULL)
    {
        int size = sizeof(CNode);

        // create a new copy of the CNode object inplace of the memory Lua
        // allocated for us
        new ((CNode*)lua_newuserdata(L,size)) CNode((const CNode&)*leftNode);
        lua_setmetatable(L, "MyLua.treenode");
    }
    else
    {
        lua_pushnil(L);
    }

    return 1;
}

我将 userdata 转换回 CTreeRoot 对象,调用 getLeftNode(),确保它存在,然后(这里是“错误的部分”)我创建了另一个 userdata 数据对象,它带有一个复制构造函数来复制我想要返回的对象。

这是针对此类场景的“标准做法”吗?您似乎希望避免创建该对象的另一个拷贝,因为您真正想要的只是对已存在对象的引用。

听起来这将是 lightuserdata 的完美位置,因为我不想创建新对象,我很乐意返回已经存在的对象。但是,问题是 lightuserdata 没有元表,所以一旦我取回这些对象,它们对我来说就没有用了。基本上,我想做类似的事情:

int MyLua::TreeRootGetLeftNode(luaState* L)
{
    CTreeRoot* root = (CTreeRoot*)luaL_checkudata(L, 1, "MyLua.treeroot");
    CNode* leftNode = root->getLeftNode();

    if (leftNode != NULL)
    {
        // "WRONG" CODE BUT SHOWS WHAT I WISH I COULD DO
        lua_pushlightuserdata(L, (void*)leftNode);
        lua_setmetatable(L, "MyLua.treenode");
    }
    else
    {
        lua_pushnil(L);
    }

    return 1;
}

谁能告诉我如何让我的 MyLua::TreeRootGetLeftNode 方法返回给 Lua 一个已经存在的对象的拷贝,这样我就可以将该对象用作“对象” ' 在 Lua 中?

最佳答案

这里可以执行两个级别的内存优化。在您的第一个功能但效率低下的解决方案中,当用户调用 getLeftNode() 时,它必须创建 CNode 的拷贝以便将其存储在 Lua 用户数据中.此外,每次用户在同一棵树上重复调用 getLeftNode() 时,它将继续创建新的用户数据来表示 CNode,即使它之前已创建。

在第一级优化中,可以memoize这个调用,这样每次用户请求同一个子树时,都可以简单的返回原来创建的userdata,而不用复制构造另一个userdata 来表示相同的东西。不过,有 3 种方法可以解决这个问题,具体取决于您是想修改 Lua 接口(interface)、更改 C++ 实现,还是硬着头皮。

  • MyLua.treenode 用户数据当前包含一个CNode 对象的实际数据。然而,这是不幸的,因为这意味着每当你创建一个 CNode 对象时,你必须使用 placement new 将它存储到创建时立即由 Lua 分配的内存中。可能更好的方法是在 MyLua.treenode 的用户数据中简单地存储一个指针(CNode*)。这确实需要您修改 MyLua.treenode 的 Lua 接口(interface),以便它现在将其数据视为指向 CNode 对象的指针。

  • 如果您希望将 CNode 数据直接存储在 MyLua.treenode 用户数据中,那么您必须确保在创建您的 CTreeRoot,它会使用 placement new 每次从 Lua 分配的内存中构造 CNode(或者你可以使用 C++ 标准库中使用的 allocator pattern?)。然而,这不太优雅,因为您的 CNode 实现现在取决于 Lua 运行时,我不推荐这样做。

  • 如果以上两种方案都不合适,那么每次返回子节点都只需要复制一份,不过对于在同一节点上重复调用还是可以提高效率的 em> 通过跟踪您之前是否创建了相同的用户数据(例如,使用 Lua 表)。

在第二级优化中,您可以通过使复制的子树成为对原始树的片段的弱引用 来进一步节省内存。这样,无论何时复制子树,都只是创建一个指向原始树的一部分的指针。或者,如果您希望您的子树即使在原始树被销毁后仍然存在,您也可以使用强引用,但是您将不得不进入引用计数的血淋淋的细节。在任何一种情况下,这种优化都纯粹是在 C++ 级别上进行的,与 Lua 接口(interface)无关,从您的代码来看,我假设您已经在使用弱引用 (CNode*)。

注意:除非在内部实现中使用,否则最好避免使用轻型用户数据。原因是轻型用户数据本质上等同于 C 指针,因此可以指向任何东西。如果您将轻量级用户数据暴露给 Lua 代码,您将不知道轻量级用户数据可能来自哪里或它包含什么类型的数据,使用它会带来安全风险(以及程序段错误的可能性)。 适当使用轻型用户数据的方法是将其用作存储在 Lua 注册表中的 Lua 查找表的索引,它可用于实现前面提到的内存。

关于c++ - 将已存在的对象从 C++ 返回到 Lua,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14788319/

有关c++ - 将已存在的对象从 C++ 返回到 Lua的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  6. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  7. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  8. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  9. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  10. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

随机推荐