【问题标题】:luabind: How to pass value from C++ to lua function by reference?luabind:如何通过引用将值从 C++ 传递给 lua 函数?
【发布时间】:2014-09-25 18:58:57
【问题描述】:

在 C++ 上编程时,您可以执行以下操作:

void byReference(int &y)
{
    y = 5;
}

int main()
{
   int x = 2;      // x = 2
   byReference(x); // x = 5
}

如何使用 luabind 做同样的事情?

Luabind 文档says:

如果你想传递一个参数作为引用,你必须把它包装起来 与Boost.Ref。

像这样:

int ret = call_function(L, "fun", boost::ref(val));

但是当我尝试这样做时:

#include <iostream>
#include <conio.h>

extern "C" 
{
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
}

#include <luabind\luabind.hpp>

using namespace std;
using namespace luabind;

int main() 
{
  lua_State *myLuaState = luaL_newstate();
  open(myLuaState);
  int x = 2;
  do
  {
    luaL_dofile(myLuaState, "script.lua");
    cout<<"x before = "<< x <<endl;
    call_function<void>(myLuaState, "test", boost::ref(x));
    cout<<"x after = "<< x <<endl;
  } while(_getch() != 27);
  lua_close(myLuaState);
}

script.lua

function test(x)
    x = 7
end

我的程序在运行时崩溃并出现以下错误:

0x76B5C42DLuaScripting.exe 中的未处理异常:Microsoft C++ 异常:std::runtime_error 位于内存位置 0x0017F61C。

那么,如何通过引用将值从 C++ 传递给 lua 函数,这样我可以在脚本中更改它,它也会在 C++ 程序中更改?我正在使用 boost 1.55.0、lua 5.1、luabind 0.9.1

编辑:

当我尝试捕捉时

try {
call_function<void>(myLuaState, "test", boost::ref(x));
}catch(const std::exception &TheError) {
cerr << TheError.what() << endl;

它给了我一个"Trying to use unregistered class" 错误。

编辑 2:

经过一番研究,我发现"Trying to use unregistered class" 抛出错误是因为boost::ref(x)。我注册了一个int&amp; 类(只是猜测):

  module(myLuaState)
      [
          class_<int&>("int&")
      ];

和"Trying to use unregistered class" 错误消失了。但是在test() 中调用print(x) 仍然会导致"lua runtime error",并且程序仍然没有做我想做的事情。

【问题讨论】:

  • &lt;void&gt; 在这里做什么:call_function&lt;void&gt;?
  • 这是一个模板函数:template&lt;class Ret&gt; Ret call_function(lua_State* L, const char* name, ...)你必须指定返回类型
  • 如果你在 test() 中打印 x 并尝试在没有 ref() 的情况下调用它会怎样?
  • @JohnZwinck function test(x) x = 7 print(x) end 它给了我“lua运行时错误”。奇怪

标签: c++ boost lua pass-by-reference luabind


【解决方案1】:

我已经设法让这件事发挥作用。好吧,与问题中的不完全一样:我传递了我的自定义类型 GameObject 而不是 int。

但由于某种原因,我仍然无法使其适用于 int、float 等简单类型。

所以,首先我决定自己构建 luabind 和 lua,以确保它们可以在 VS2012 中工作。我按照question 的说明进行操作。

然后我写了这个测试程序:

#include <iostream>
#include <conio.h>

extern "C" 
{
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
}

#include <luabind\luabind.hpp>
#include <luabind\adopt_policy.hpp>

using namespace std;
using namespace luabind;

struct Vector2
{
    float x, y;
};

class Transform
{
public:
    Transform()
    {
        pos.x = 0;
        pos.y = 0;
    }

public:
    Vector2 pos;
};

class Movement
{
public:
    Movement(){}

public:
    Vector2 vel;
};

class GameObject
{
public:
    GameObject(){}

    Transform& getTransform()
    {
        return _transform;
    }

    Movement& getMovement()
    {
        return _movement;
    }

private:
    Transform _transform;
    Movement  _movement;
};

int main()
{
    lua_State *myLuaState = luaL_newstate();

    open(myLuaState);

    module(myLuaState) [
      class_<Vector2>("Vector2")
          .def(constructor<>())
          .def_readwrite("x", &Vector2::x)
          .def_readwrite("y", &Vector2::y),

      class_<Transform>("Transform")
          .def(constructor<>())
          .def_readwrite("pos", &Transform::pos),

      class_<Movement>("Movement")
          .def(constructor<>())
          .def_readwrite("vel", &Movement::vel),

      class_<GameObject>("GameObject")
          .def(constructor<>())
          .def("getTransform", &GameObject::getTransform)
          .def("getMovement", &GameObject::getMovement)
    ];

    GameObject _testGO;

    _testGO.getMovement().vel.x = 2;
    _testGO.getMovement().vel.y = 3;

    do
    {
        cout<<"_testGO.pos.x before = "<< _testGO.getTransform().pos.x <<endl;
        cout<<"_testGO.pos.y before = "<< _testGO.getTransform().pos.y <<endl;

        try 
        {
            luaL_dofile(myLuaState, "script.lua");

            call_function<void>(myLuaState, "testParams", boost::ref(_testGO), 0.3);
        }
        catch(const exception &TheError)
        {
            cerr << TheError.what() << endl;
        }

        cout<<"_testGO.pos.x after = "<< _testGO.getTransform().pos.x <<endl;
        cout<<"_testGO.pos.y after = "<< _testGO.getTransform().pos.y <<endl;
    }
    while(_getch() != 27);

    lua_close(myLuaState);

    return 0;
}

script.lua:

function testParams(owner, dt)
    owner:getTransform().pos.x = owner:getMovement().vel.x * dt;
    owner:getTransform().pos.y = owner:getMovement().vel.y * dt;
end

它奏效了:

_testGO.pos.x before = 0
_testGO.pos.y before = 0
_testGO.pos.x after = 0.6
_testGO.pos.y after = 0.9

将尝试弄清楚如何管理简单类型。

更新:

这是同一个问题的答案,我进入了 luabind 邮件列表:

'不可能通过引用传递在 lua 中建模的具有内置类型的对象类型。在 Lua 中,不存在引用或值类型等类型的属性。类型要么是引用类型(表),要么是值类型(内置类型,如数字)。 但是,您可以做的是在 lua 端返回新值。可以建立一个策略,将引用整数类型转换为已解析返回类型的列表,因此它在 c++ 端是透明的。不过,这会对 lua 方面施加严格的限制。' 链接:http://sourceforge.net/p/luabind/mailman/message/32692053/

【讨论】:

  • 我没有用过luabind,但是通过引用传递参数似乎与Lua的设计相悖。 Lua按值传递参数(如Java);值是原语或引用。即使我能做到,我也不会这样做。您的工作解决方案更自然。
【解决方案2】:

一般来说,在 lua 中,如果您想模拟引用变量/表,您可以将其声明为全局变量,然后在函数中使用它。

-- global table t
t = {}
-- insert the value of val into t by index or by property
-- aGenericFunction1(val[, property])
function aGenericFunction1(val, property)
   if property then
       t[property] = val
   else
       table.insert(t, val)
   end
end

这并不完全一样,但它是一个简单的解决方法。

现在每次调用aGenericFunction1它都会通过属性或索引更新t表。

Example:
    aGenericFunction1("Barks!", "dog")
    print(t.dog)
    -- prints Barks!

同样的事情也可以用于变量...

-- global variable x
x = 0
-- insert the value of val into x
function aGenericFunction2(val)
   x = val
end

同样的,每次调用aGenericFunction2都会覆盖x

 Example:
     aGenericFunction2(4)
     print(x)
     -- prints 4

因此,本质上 t 或 x 将成为参考,具体取决于您想要完成的任务。 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-21
    • 2011-12-07
    • 2017-08-12
    • 2018-09-22
    • 2018-01-18
    • 1970-01-01
    相关资源
    最近更新 更多