简体   繁体   中英

luabind: How to pass value from C++ to lua function by reference?

When programming on C++, you can do the following:

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

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

How to do the same using luabind?

Luabind docs says :

If you want to pass a parameter as a reference, you have to wrap it with the Boost.Ref .

Like this:

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

But when I'm trying to do this:

#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

My program crashes at runtime with the following error:

Unhandled exception at at 0x76B5C42D in LuaScripting.exe : Microsoft C++ exception: std::runtime_error at memory location 0x0017F61C .

So, how to pass value from C++ to lua function by reference, so I can change it inside the script and it will be changed in c++ program too? I'm using boost 1.55.0, lua 5.1, luabind 0.9.1

EDIT :

When I try-catched

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

it gave me a "Trying to use unregistered class" error.

EDIT 2 :

After a little research, I found that "Trying to use unregistered class" error throwing because of boost::ref(x) . I registered a int& class(just a guess):

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

and "Trying to use unregistered class" error disappeared. But calling print(x) in test() still causes "lua runtime error" , and program still not doing what I want from it to do.

I've managed to make this thing work. Well, not exactly like in the question: instead of int I passed my custom type GameObject .

But for some reason I still can't make it work with simple types like int , float etc.

So, first I decided to build luabind and lua by myself, just to be sure that they will work in VS2012. I followed the instructions from this question .

Then I wrote this test program:

#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

And it worked:

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

Will try to figure out how to manage simple types.

UPDATE:

This is the answer for the same question, I got on the luabind mailing lists:

'It is impossible to pass object types that are modelled in lua with built-in types by reference. In Lua, there exists no attribute to types like reference or value type. A type is either a reference type (tables), or a value type (built-in types like number). What you can do however is return the new value in the lua-side. A policy could be established, that transforms reference integral types to a list of parsed return types, so it is transparent on the c++ side. That would place hard constraints on the lua side though.' link: http://sourceforge.net/p/luabind/mailman/message/32692053/

In lua in general if you want to emulate referencing a variable/table you would declare it global and then use it in a function.

-- 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

Its not exactly the same thing but its a simple work around.

Now every time you call aGenericFunction1 it will update the t table either by property or index.

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

Same thing can be accomplished for variables...

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

Same thing, every time you call aGenericFunction2 it will overwrite x

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

So in essence either t or x would become the reference depending on what you want to accomplish. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM