Skip to content

Instantly share code, notes, and snippets.

@ckytam
Forked from kklouzal/LuaClassBinding.cpp
Created May 5, 2024 02:28
Show Gist options
  • Save ckytam/38f455c0f2ac174d5260d864fdd1b54e to your computer and use it in GitHub Desktop.
Save ckytam/38f455c0f2ac174d5260d864fdd1b54e to your computer and use it in GitHub Desktop.
Binding A C++ Class To Lua The Manual Way (shared_ptr memory management)
#include <lua.hpp> // LuaJIT header
#include <iostream> // Access to std::system("PAUSE")
#include <memory>
// A simple class to be bound in our Lua environment.
class MyClass
{
public:
MyClass()
{
printf("Created New MyClass\n");
}
~MyClass()
{
printf("Destroyed Existing MyClass\n");
}
void SomeFunction(const char* PassedString)
{
printf("MyClass: %s\n", PassedString);
}
};
// Accessor function so the Lua Garbage Collector can properly free our resources.
static int MyClass_Destructor(lua_State *L)
{
auto Obj = static_cast<std::shared_ptr<MyClass>*>(lua_touserdata(L, -1));
Obj->reset();
return 0;
}
// Accessor function to pull our class object out of the userdata
// then use it to call the appropriate member function.
static int MyClass_SomeFunction(lua_State *L)
{
if (lua_isuserdata(L, -1))
{
auto Obj = static_cast<std::shared_ptr<MyClass>*>(lua_touserdata(L, -1));
Obj->get()->SomeFunction("Hello Poppet!");
}
else {
printf("[Lua Error]: Tried to access non-userdata from the stack.");
}
return 0;
}
// This function will register our MyClass metatable into the Lua environment
// so we can later attach it to our userdata.
void RegisterMyClass(lua_State *L)
{
static const luaL_Reg MyClass_Methods[] = {
{ "__gc", MyClass_Destructor },
{ "SomeFunction", MyClass_SomeFunction },
{ NULL, NULL },
};
// Create an empty metatable and push it onto the stack.
// NOTE: The name does NOT need to match the name of our static struct above
// this is only done for readability
luaL_newmetatable(L, "MyClass_Methods");
// Duplicate the metatable so 2 now exist on the stack.
lua_pushvalue(L, -1);
/* Pop the first metatable off the stack and assign it to __index
* of the second one. We set the metatable for the table to itself.
* This is equivalent to the following in lua:
* metatable = {}
* metatable.__index = metatable
*/
lua_setfield(L, -2, "__index");
// Add our methods into the metatable.
luaL_register(L, NULL, MyClass_Methods);
// Clear the stack
//lua_remove(L, -1);
}
// Here we have a function which instantiates userdata to represent our
// MyClass object, sets the appropriate metatable, and returns it to the Lua environment.
static int MyClass_Create(lua_State *L)
{
// Push our userdata onto the stack.
size_t ObjectSize = sizeof(std::shared_ptr<MyClass>);
void* Memory = lua_newuserdata(L, ObjectSize);
auto resource = std::make_shared<MyClass>();
new(Memory) std::shared_ptr<MyClass>(resource);
// Push our metatable onto the stack.
luaL_getmetatable(L, "MyClass_Methods");
// Pop the metatable off the stack and set apply it to our userdata.
lua_setmetatable(L, -2);
// Return our userdata back to the Lua environment.
return 1;
}
int main()
{
printf("Mark Startup Memory\n");
std::system("PAUSE");
// Create the Lua State/Environment and load the core libraries.
lua_State *L = lua_open();
luaL_openlibs(L);
// Register our bound class.
RegisterMyClass(L);
// Register the creation func.
lua_pushcfunction(L, MyClass_Create);
lua_setglobal(L, "NewMyClass");
// Run our supplied 'main.lua' file and report back any script errors.
if (luaL_loadfile(L, "main.lua") || lua_pcall(L, 0, 0, 0))
{
printf("[Lua Error]: %s\n", lua_tostring(L, -1));
}
// Finally close our State/Environment and pause the application.
lua_close(L);
printf("Mark Shutdown Memory\n");
std::system("PAUSE");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment