Using direct Lua, how do I show an existing C ++ class object for use in a Lua script?

I am promoting my experience implementing Lua scripts in C ++, and I could use my hand here.

Consider the following two classes:

// 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; } }; 

... and ...

 // 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() { /* ... */ } }; 

... where getPerson checks the pm_People vector to see if a person with the given name personExists using personExists .

Now consider the following function, which receives a Person object from Lua and returns its age.

 // 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 age onto the Lua stack. lua_pushnumber(LS, luaPerson->getAge()); // Return that age integer. return 1; } 

I want to do this to get the already created and existing Person object from the PersonManager using getPerson and expose this object in Lua, so I can do something like this:

 local testPerson = People.get("Stack Overflower") print(testPerson:getAge()) 

I tried something like a block of code below, to no avail:

 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; } 

Can someone lend a helping hand or at least point me in the right direction? I do not use any lua shell libraries, just straight Lua.

Thanks.

EDIT: The functions that I use to PersonManager my Person and PersonManager are as follows:

 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); } 
+7
c ++ lua
source share
2 answers

Let the top begin, that is, registering PersonManager in Lua. Since this is a singleton, we will register it as global.

 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 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 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"); } 

Now we are processing the PersonManager get method:

 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); } 

And finally, processing Person getAge .

 int lua_Person_getAge(lua_State *lua) { Person **pptr = (Person**)lua_checkudata(lua, 1, "PersonMetaTable"); lua_pushnumber(lua, (*pptr)->getAge()); return 1; } 

Now you should call registerPersonManager before executing the Lua code, best after creating a new Lua state and opening the necessary libraries.

Now in Lua you can do this:

 local person = PersonManager:getPerson("Stack Overflower"); print(person:getAge()); 

At the moment, I don't have access to either Lua or C ++ to test it, but this should get you started. Please be careful with the lifetime of the Person pointer to which you provide Lua.

+7
source share

You are using a full userdata containing a record that points to light user data. Light user data is values ​​that can only be created from C / C ++, they are similar to a number in Lua, since they have no methods, metatets, etc. Then, when your C ++ functions receive full user data, they receive a pointer from it, which can then be used to access the C ++ methods of the underlying C ++ object.

See Accessing Light user data in Lua and the links there and see if you can handle it. The Lua news archive also has many posts that you can find through google.

Please note that using SWIG to create shell code for you this task will be trivial, and you can focus on your application, and not on the binding of C ++ and Lua.

0
source share

All Articles