如何从Lua访问运行Lua脚本的类的变量 [英] How to access variables of the class that runs the Lua script from Lua

查看:113
本文介绍了如何从Lua访问运行Lua脚本的类的变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否有可能从Lua脚本中使用的绑定C ++类中访问运行Lua脚本的类的变量.

I wonder if it's possible to access the variables of the class that runs the Lua script from the bound C++ class that is being used in Lua script.

从下面的示例中,我想知道是否有可能以某种方式从绑定的Test类中访问myLua类中的name变量.

From the example below, I wonder if it's possible to access name variable in myLua class from the bound Test class somehow.

这是我的密码.

main.cpp:

extern "C" 
{
    int luaopen_my(lua_State* L);
}

class myLua {

public:

    struct myData
    {
        std::string name;
        lua_State *L;
    };
    myLua(std::string name)
    {
        data = make_shared<myData>();
        data->name = name;
        data->L = luaL_newstate();
        lua_State *L = data->L;
        luaL_openlibs(L);
        luaopen_my(L);
        lua_settop(L, 0);

        const char *script =
        "function setup() \
           test = my.Test() \
           test:callHello() \
         end \
         function hello(name) \
           print('hello is called by : ' .. name) \
         end";
        //------------Added----------------
        lua_pushlightuserdata(L, data.get());
        myLua::myData *b = static_cast<myLua::myData *>(lua_touserdata(L, 1));
        cout << "RESULT1 : " << b->name << endl;
        //---------------------------------

        const int ret = luaL_loadstring(L, script);
        if (ret != 0 || lua_pcall(L, 0, LUA_MULTRET, 0) != 0)
        {
            std::cout << "failed to run lua script" << std::endl;
            return;
        }
        lua_getglobal(L, "setup");
        if (lua_pcall(L, 0, 0, 0))
        {
            std::cout << "failed to call setup function" << std::endl;
            return;
        }
    }
    shared_ptr<myData> data;
};

void main() 
{
    myLua lua1("Apple");
    myLua lua2("Orange");
}

bindings.h:

bindings.h :

class Test
{
public:
    void callHello(lua_State *L) {

        //------------Added----------------
        myLua::myData *b = static_cast<myLua::myData *>(lua_touserdata(L, -1));
        cout << "RESULT2 : " << b->name << endl;
        //---------------------------------

        lua_getglobal(L, "hello");
        lua_pushstring(L, "ClassName");
        if (lua_pcall(L, 1, 0, 0))
        {
            std::cout << "failed to call hello function" << std::endl;
            return;
        }
    };
};

bindings.i :(用于通过SWIG绑定bindings.h)

bindings.i : (Used to bind bindings.h using SWIG)

%module my
%{
    #include "bindings.h"
%}

%include <stl.i>
%include <std_string.i>
%include <std_vector.i>
%include <std_map.i>
%include <typemaps.i>

%typemap(default) (lua_State *L) 
{
    $1 = L;
}
typedef std::string string;

%include "bindings.h"

当前结果:

hello is called by : ClassName
hello is called by : ClassName

我想要的结果:

hello is called by : Apple
hello is called by : Orange

也许我可以通过某种方式将变量注册到lua_State*?

Maybe I can register the variable to lua_State* somehow?

如果有类似的东西,我认为那太好了

I think it would be great if there's something like

lua_registerdata(L, &name);

然后使用类似的东西

string name = lua_getregistereddata(L);

添加了代码的结果:

RESULT1 : Apple
RESULT2 : \360n\240\300`\255\276\255\336\336\300ݺ\220\300`DD\255\276\255\336\336\300ݺ\300\217\300`\340_\300`D\376
hello is called by : ClassName
RESULT1 : Orange
RESULT2 : \360n\300`\255\276\255\336\336\300ݺ\200\236\300`DD\255\276\255\336\336\300ݺ@\236\300``w\300`D\376
hello is called by : ClassName

推荐答案

按值传递

我建议您将name作为参数传递给setupcallHello.这就解决了对象寿命的问题.

Pass by value

I suggest that you pass the name as an argument to setup and callHello. That solves the problem with lifetime of objects.

N.B.:从C ++调用Lua函数,然后从Lua调用C ++函数似乎效率很低.您确定您的设计吗?您真的需要通过Lua进行这种额外的间接访问吗?

N.B.: Calling a Lua function from C++ which then calls a C++ function from Lua seems very inefficient. Are you sure about your design? Do you really need this extra indirection through Lua?

bindings.h

#pragma once

#include <iostream>
#include <string>

class Test {
public:
    void callHello(std::string const &name, lua_State *L) {
        lua_getglobal(L, "hello");
        lua_pushstring(L, name.c_str());
        if (lua_pcall(L, 1, 0, 0) != 0) {
            std::cout << "failed to call hello function\n"
                      << lua_tostring(L, -1) << '\n';
            return;
        }
    }
};

test.cpp

#include <iostream>
#include <string>

#include <lua.hpp>

extern "C" int luaopen_my(lua_State *L);

class myLua {
public:
    myLua(std::string const &name) {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        luaopen_my(L);

        const char *script = "function setup(name)\n"
                             "    local test = my.Test()\n"
                             "    test:callHello(name)\n"
                             "end\n"
                             "function hello(name)\n"
                             "    print('hello is called by : ' .. name)"
                             "end";

        if (luaL_dostring(L, script) != 0) {
            std::cout << "failed to run lua script\n"
                      << lua_tostring(L, -1) << '\n';
            lua_close(L);
            return;
        }

        lua_getglobal(L, "setup");
        lua_pushstring(L, name.c_str());
        if (lua_pcall(L, 1, 0, 0) != 0) {
            std::cout << "failed to call setup function\n"
                      << lua_tostring(L, -1) << '\n';
            lua_close(L);
            return;
        }

        lua_close(L);
    }
};

int main() {
    myLua lua1("Apple");
    myLua lua2("Orange");
}

通过lightuserdata传递

根据您的要求,您还可以将指向字符串的指针作为lightuserdata推送到注册表中,并在callHello函数中获取它.由于各种原因,使用注册表很危险.密钥可能会发生冲突,您必须绝对确保未在其他地方使用过该密钥.指向C ++数据的指针可能会晃来晃去,而Lua不会也不知道,因此会很乐意为您提供一个无效的指针.取消引用会导致难以调试的分段错误.

Pass by lightuserdata

As you have requested, you can also push a pointer to the string as lightuserdata into the registry and fetch it in the callHello function. Using the registry is dangerous for various reason. Keys might collide and you have to absolutely sure that the key hasn't been used elsewhere. The pointers to the C++ data might go dangling and Lua does not and cannot know about that and will happily hand you an invalid pointer. Dereferencing leads to a hard-to-debug segmentation fault.

N.B.:我认为这是错误的设计,应避免使用.为了不必传递参数的便利而放弃内存安全性,这似乎不是一个很好的权衡.

N.B.: I believe that this is bad design and should be avoided. Giving up memory safety for the convenience of not having to pass a parameter doesn't sound like a good trade-off.

bindings.h

#pragma once

#include <iostream>
#include <string>

class Test {
public:
    void callHello(lua_State *L) {
        // Fetch light userdata from the registry with key "name" and
        // pray that it is there
        lua_pushstring(L, "name");
        lua_gettable(L, LUA_REGISTRYINDEX);
        std::string name;
        if (lua_islightuserdata(L, -1) == 1) {
            name = *static_cast<std::string *>(lua_touserdata(L, -1));
            lua_pop(L, 1);
        } else {
            lua_pushstring(L, "userdata corrupted or absent");
            lua_error(L);
            return;
        }

        // Call hello function with fetched name
        lua_getglobal(L, "hello");
        lua_pushstring(L, name.c_str());
        if (lua_pcall(L, 1, 0, 0) != 0) {
            std::cout << "failed to call hello function\n"
                      << lua_tostring(L, -1) << '\n';
            return;
        }
    }
};

test.cpp

#include <iostream>
#include <string>

#include <lua.hpp>

extern "C" int luaopen_my(lua_State *L);

class myLua {
public:
    myLua(std::string name) {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        luaopen_my(L);

        const char *script = "function setup()\n"
                             "    local test = my.Test()\n"
                             "    test:callHello()\n"
                             "end\n"
                             "function hello(name)\n"
                             "    print('hello is called by : ' .. name)"
                             "end";

        if (luaL_dostring(L, script) != 0) {
            std::cout << "failed to run lua script\n"
                      << lua_tostring(L, -1) << '\n';
            lua_close(L);
            return;
        }

        // Push light userdata into the registry with key "name"
        lua_pushstring(L, "name");
        lua_pushlightuserdata(L, static_cast<void *>(&name));
        lua_settable(L, LUA_REGISTRYINDEX);

        lua_getglobal(L, "setup");
        if (lua_pcall(L, 0, 0, 0) != 0) {
            std::cout << "failed to call setup function\n"
                      << lua_tostring(L, -1) << '\n';
            lua_close(L);
            return;
        }

        lua_close(L);
    }
};

int main() {
    myLua lua1("Apple");
    myLua lua2("Orange");
}

公用位

SWIG界面文件不需要调整,并且在两种情况下都保持不变.

Common bits

The SWIG interface file doesn't need to be adapted and stays the same for either case.

my.i

%module my
%{
    #include "bindings.h"
%}

%include <std_string.i>
%include <typemaps.i>

%typemap(default) (lua_State *L) 
{
    $1 = L;
}

%include "bindings.h"

在两种情况下都可以使用(例如)完成编译和运行

Compile and run can be done for both cases with (for example)

$ swig -lua -c++ my.i
$ clang++ -Wall -Wextra -Wpedantic -I/usr/include/lua5.2/ my_wrap.cxx test.cpp -llua5.2
$ ./a.out 
hello is called by : Apple
hello is called by : Orange

这篇关于如何从Lua访问运行Lua脚本的类的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆