直接使用Lua,如何公开现有的c++类对象,以便在Lua脚本中使用?
Using straight Lua, how do I expose an existing C++ class objec for use in a Lua script?
我一直在c++中嵌入Lua脚本,我需要你的帮助。
考虑以下两个类:
// Person.hpp
#pragma once
#include <string>
class Person {
private:
std::string p_Name;
int p_Age;
public:
Person(const std::string & strName, const int & intAge)
: p_Name(strName), p_Age(intAge) { }
Person() : p_Name(""), p_Age(0) { }
std::string getName() const { return p_Name; }
int getAge() const { return p_Age; }
void setName(const std::string & strName) { p_Name = strName; }
void setAge(const int & intAge) { p_Age = intAge; }
};
…和…
// PersonManager.hpp
#pragma once
#include "Person.hpp"
#include <vector>
class PersonManager {
// Assume that this class is a singleton, and therefore
// has no public constructor, but a static function that returns the
// singleton instance.
private:
std::vector<Person *> pm_People;
public:
bool personExists(const std::string & strName) { /* ... */ }
bool addPerson(const std::string & strName, const int & intAge) { /* ... */ }
Person * getPerson(const std::string & strName) { /* ... */ }
void removePerson(const std::string & strName) { /* ... */ }
void removeAllPeople() { /* ... */ }
};
…其中,getPerson
使用personExists
检查pm_People
向量,查看具有指定名称的人是否存在。
现在,考虑下面的函数,它从Lua获取Person
对象并返回其年龄。
// Lua_Person.cpp
#include "Lua_Person.hpp" // "Lua_Person.hpp" declares the function called to expose the "Person" functions to Lua.
#include "PersonManager.hpp"
#include "Person.hpp"
int lua_GetPersonAge(lua_State * LS) {
// Validate the userdata.
luaL_checktype(LS, 1, LUA_TUSERDATA);
// Get the "Person" userdata.
Person * luaPerson = reinterpret_cast<Person *>(lua_touserdata(LS, 1));
// Check to see if the Person pointer is not null.
if(luaPerson == nullptr)
luaL_error(LS, "lua_GetPersonAge: You gave me a null pointer!");
// Push the person's age onto the Lua stack.
lua_pushnumber(LS, luaPerson->getAge());
// Return that age integer.
return 1;
}
我想做的是从PersonManager
单例中获得一个已经实例化和存在的Person
对象,使用getPerson
,并将该对象暴露给Lua,所以我可以这样写:
local testPerson = People.get("Stack Overflower")
print(testPerson:getAge())
我尝试了下面的代码块,但没有效果:
int lua_GetPerson(lua_State * LS) {
// Validate the argument passed in.
luaL_checktype(LS, 1, LUA_TSTRING);
// Get the string.
std::string personName = lua_tostring(LS, 1);
// Verify that the person exists.
if(PersonManager::getInstance().personExists(personName) == false)
luaL_error(LS, "lua_GetPerson: No one exists with this ID: %s", personName.c_str());
// Put a new userdata into a double pointer, and assign it to the already existing "Person" object requested.
Person ** p = static_cast<Person **>(lua_newuserdata(LS, sizeof(Person *))); // <Userdata>
*p = PersonManager::getInstance().getPerson(personName);
// Put that person object into the "Meta_Person" metatable.
// Assume that metatable is created during the registration of the Person/Person Manager functions with Lua.
luaL_getmetatable(LS, "Meta_Person"); // <Metatable>, <Userdata>
lua_setmetatable(LS, -2); // <Metatable>
// Return that metatable.
return 1;
}
有谁能帮帮我,或者至少给我指个方向吗?我没有使用任何lua包装库,只是直接使用lua。
谢谢。
编辑:我用来暴露Person
和PersonManager
函数的函数如下:
void exposePerson(lua_State * LS) {
static const luaL_reg person_functions[] = {
{ "getAge", lua_getPersonAge },
{ nullptr, nullptr }
};
luaL_newmetatable(LS, "Meta_Person");
lua_pushstring(LS, "__index");
lua_pushvalue(LS, -2);
lua_settable(LS, -3);
luaL_openlib(LS, nullptr, person_functions, 0);
}
void exposePersonManager(lua_State * LS) {
static const luaL_reg pman_functions[] = {
{ "get", lua_getPerson },
{ nullptr, nullptr }
};
luaL_openlib(LS, "People", pman_functions, 0);
lua_pop(LS, 1);
}
让我们从顶部开始,即在Lua中注册PersonManager
。因为它是单例的,所以我们将它注册为全局的。
void registerPersonManager(lua_State *lua)
{
//First, we create a userdata instance, that will hold pointer to our singleton
PersonManager **pmPtr = (PersonManager**)lua_newuserdata(
lua, sizeof(PersonManager*));
*pmPtr = PersonManager::getInstance(); //Assuming that's the function that
//returns our singleton instance
//Now we create metatable for that object
luaL_newmetatable(lua, "PersonManagerMetaTable");
//You should normally check, if the table is newly created or not, but
//since it's a singleton, I won't bother.
//The table is now on the top of the stack.
//Since we want Lua to look for methods of PersonManager within the metatable,
//we must pass reference to it as "__index" metamethod
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
//lua_setfield pops the value off the top of the stack and assigns it to our
//field. Hence lua_pushvalue, which simply copies our table again on top of the stack.
//When we invoke lua_setfield, Lua pops our first reference to the table and
//stores it as "__index" field in our table, which is also on the second
//topmost position of the stack.
//This part is crucial, as without the "__index" field, Lua won't know where
//to look for methods of PersonManager
luaL_Reg personManagerFunctions[] = {
'get', lua_PersonManager_getPerson,
nullptr, nullptr
};
luaL_register(lua, 0, personManagerFunctions);
lua_setmetatable(lua, -2);
lua_setglobal(lua, "PersonManager");
}
现在处理PersonManager
的get
方法:
int lua_PersonManager_getPerson(lua_State *lua)
{
//Remember that first arbument should be userdata with your PersonManager
//instance, as in Lua you would call PersonManager:getPerson("Stack Overflower");
//Normally I would first check, if first parameter is userdata with metatable
//called PersonManagerMetaTable, for safety reasons
PersonManager **pmPtr = (PersonManager**)luaL_checkudata(
lua, 1, "PersonManagerMetaTable");
std::string personName = luaL_checkstring(lua, 2);
Person *person = (*pmPtr)->getPerson(personName);
if (person)
registerPerson(lua, person); //Function that registers person. After
//the function is called, the newly created instance of Person
//object is on top of the stack
else
lua_pushnil(lua);
return 1;
}
void registerPerson(lua_State *lua, Person *person)
{
//We assume that the person is a valid pointer
Person **pptr = (Person**)lua_newuserdata(lua, sizeof(Person*));
*pptr = person; //Store the pointer in userdata. You must take care to ensure
//the pointer is valid entire time Lua has access to it.
if (luaL_newmetatable(lua, "PersonMetaTable")) //This is important. Since you
//may invoke it many times, you should check, whether the table is newly
//created or it already exists
{
//The table is newly created, so we register its functions
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
luaL_Reg personFunctions[] = {
"getAge", lua_Person_getAge,
nullptr, nullptr
};
luaL_register(lua, 0, personFunctions);
}
lua_setmetatable(lua, -2);
}
最后处理Person
的getAge
。
int lua_Person_getAge(lua_State *lua)
{
Person **pptr = (Person**)lua_checkudata(lua, 1, "PersonMetaTable");
lua_pushnumber(lua, (*pptr)->getAge());
return 1;
}
你现在应该在执行你的Lua代码之前调用registerPersonManager
,最好是在你创建新的Lua状态和打开所需的库之后。
现在在Lua中,你应该可以这样做:
local person = PersonManager:getPerson("Stack Overflower");
print(person:getAge());
我目前没有Lua或c++来测试它,但这应该让你开始。请注意你给Lua访问的Person
指针的生命周期。
您使用一个完整的userdata,其中包含一个指向轻userdata的指针条目。轻用户数据是只能从C/c++中创建的值,它们就像Lua中的数字,因为它们没有方法、元表等。然后,无论何时c++函数获得完整的用户数据,它们都会从中获得指针,然后可以使用该指针访问底层c++对象的c++方法。
参见在Lua中访问Light userdata和那里的链接,看看你是否可以解决它。在Lua新闻组存档中也有许多帖子,您可以通过google找到。
请注意,使用SWIG为您生成包装器代码,此任务将是微不足道的,您可以专注于您的应用程序,而不是绑定c++和Lua。
- 使用 LuaBridge 或 Lua 将对象传递给 Lua 函数C++
- 使用 Lua 中的C++库对象
- Lua C API - 从C++分配和使用类的对象成员
- 在脚本运行后引用 lua 表/对象
- 如何通过LightUserData对象将表参数从LUA传递到C
- 使用Lua定义C++游戏中对象的变量
- 如何在LUA中创建一个C++兼容的函数对象
- 如何推动在LUA脚本上使用的现有/创建/实例化类对象
- 而不是在 lua 中创建一个对象,如何让 lua 直接高C++对象来启动方法?
- 我应该如何编写用C++代码编写的Lua API/对象模型的文档
- 无法将Boost.Any对象传递到C++Lua绑定
- 对象与静态成员的lua/C++绑定
- luabind获取c++对象的类元表,将obj作为参数推送到lua函数
- 将Lua类对象存储到C指针,并将其推送回Lua Stack
- 在 Lua 5.2 中返回 C++ 对象的实例
- 在 c++ 中使用 lua 对象
- Lua 对象内省
- Lua 5.2 - C++对象中的对象(使用 lua_lightuserdata)
- 无法修改传递给 lua 的对象
- 我的 Lua 对象正在收集中