我在使用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
使用pm_People
检查personExists
向量,以查看具有指定名称的人是否存在。
现在,考虑以下函数从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;
}
我想要做的是使用Person
从PersonManager
单例中获取已经实例化的现有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);
}
答案 0 :(得分:7)
让我们从顶部开始,即在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
的{{1}}方法:
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);
}
的{{1}}。
Person
现在,您应该在执行Lua代码之前调用getAge
,最好在创建新的Lua状态并打开所需的库之后。
现在在Lua内,您应该可以这样做:
int lua_Person_getAge(lua_State *lua)
{
Person **pptr = (Person**)lua_checkudata(lua, 1, "PersonMetaTable");
lua_pushnumber(lua, (*pptr)->getAge());
return 1;
}
我目前无法使用Lua或C ++来测试它,但这应该让你开始。请注意您为Lua访问的registerPersonManager
指针的生命周期。
答案 1 :(得分:0)
您使用包含指向轻用户数据指针的条目的完整用户数据。 Light userdata是只能用C / C ++创建的值,它们就像Lua中的一个数字,因为它们没有方法,metatable等等。然后每当你的C ++函数获得完整的用户数据时,它们就会得到它的指针,然后可以用来访问底层C ++对象的C ++方法。
请参阅Accessing Light userdata in Lua及其中的链接,看看是否可以解决问题。您可以通过谷歌找到Lua新闻组档案中的许多帖子。
请注意,使用SWIG为您生成包装器代码,此任务将是微不足道的,您可以专注于您的应用程序而不是绑定C ++和Lua。