Russel Ledge Russel Ledge - 3 months ago 24
C++ Question

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.

Answer

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/

Comments