如何从 Lua 访问运行 Lua 脚本的 class 的变量
How to access variables of the class that runs the Lua script from Lua
我想知道是否可以从 Lua 脚本中使用的绑定 C++ class 访问运行 Lua 脚本的 class 的变量.
从下面的示例中,我想知道是否可以通过某种方式从绑定 Test
class 访问 myLua
class 中的 name
变量。
这是我的代码。
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 :
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
)
%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)
{
= 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*
?
我觉得如果有类似的东西就好了
lua_registerdata(L, &name);
然后使用类似
的方式获取它
string name = lua_getregistereddata(L);
添加代码后的结果:
RESULT1 : Apple
RESULT2 : 0n00`565660ݺ00`DD565660ݺ070`0_0`D6
hello is called by : ClassName
RESULT1 : Orange
RESULT2 : 0n0`565660ݺ060`DD565660ݺ@60``w0`D6
hello is called by : ClassName
按值传递
我建议您将 name
作为参数传递给 setup
和 callHello
。这解决了对象生命周期的问题。
N.B.: 从 C++ 调用 Lua 函数,然后从 Lua 调用 C++ 函数似乎非常低效。你确定你的设计吗?您真的需要通过 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 不知道也不能知道这一点,并且会很乐意给你一个无效的指针。取消引用会导致难以调试的分段错误。
N.B.: 我认为这是糟糕的设计,应该避免。为了不必传递参数的便利而放弃内存安全听起来不是一个好的权衡。
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 接口文件不需要调整,在任何情况下都保持不变。
my.i
%module my
%{
#include "bindings.h"
%}
%include <std_string.i>
%include <typemaps.i>
%typemap(default) (lua_State *L)
{
= L;
}
%include "bindings.h"
编译和运行两种情况都可以完成(例如)
$ 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 脚本中使用的绑定 C++ class 访问运行 Lua 脚本的 class 的变量.
从下面的示例中,我想知道是否可以通过某种方式从绑定 Test
class 访问 myLua
class 中的 name
变量。
这是我的代码。
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 :
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
)
%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)
{
= 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*
?
我觉得如果有类似的东西就好了
lua_registerdata(L, &name);
然后使用类似
的方式获取它string name = lua_getregistereddata(L);
添加代码后的结果:
RESULT1 : Apple
RESULT2 : 0n00`565660ݺ00`DD565660ݺ070`0_0`D6
hello is called by : ClassName
RESULT1 : Orange
RESULT2 : 0n0`565660ݺ060`DD565660ݺ@60``w0`D6
hello is called by : ClassName
按值传递
我建议您将 name
作为参数传递给 setup
和 callHello
。这解决了对象生命周期的问题。
N.B.: 从 C++ 调用 Lua 函数,然后从 Lua 调用 C++ 函数似乎非常低效。你确定你的设计吗?您真的需要通过 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 不知道也不能知道这一点,并且会很乐意给你一个无效的指针。取消引用会导致难以调试的分段错误。
N.B.: 我认为这是糟糕的设计,应该避免。为了不必传递参数的便利而放弃内存安全听起来不是一个好的权衡。
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 接口文件不需要调整,在任何情况下都保持不变。
my.i
%module my
%{
#include "bindings.h"
%}
%include <std_string.i>
%include <typemaps.i>
%typemap(default) (lua_State *L)
{
= L;
}
%include "bindings.h"
编译和运行两种情况都可以完成(例如)
$ 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