I've been furthering my experience in embedding Lua scripting in C++, and I could use a 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 the person with the specified name exists, using personExists
.
Now, consider the following function that gets 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's age onto the Lua stack.
lua_pushnumber(LS, luaPerson->getAge());
// Return that age integer.
return 1;
}
What I want to do is to get an already-instantiated and existing Person
object from the PersonManager
singleton, using getPerson
, and expose that object to Lua,
so I can do something like this:
local testPerson = People.get("Stack Overflower")
print(testPerson:getAge())
I tried something like the code block 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 anybody lend a helping hand here, or at least point me in the right direction? I am not using any lua wrapper libraries, just straight Lua.
Thank you.
EDIT: The functions that I use to expose my Person
and PersonManager
functions 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);
}