c++boostluapass-by-referenceluabind

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.


Solution

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