Last active
August 27, 2024 06:32
-
-
Save Youka/2a6e69584672f7cb0331 to your computer and use it in GitHub Desktop.
Example of Lua in C++ and userdata objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Lua C API | |
#include <lua.hpp> | |
// C++ input/output streams | |
#include <iostream> | |
// MyObject as C++ class | |
class MyObject{ | |
private: | |
double x; | |
public: | |
MyObject(double x) : x(x){} | |
void set(double x){this->x = x;} | |
double get() const{return this->x;} | |
}; | |
// MyObject identifier for the Lua metatable | |
#define LUA_MYOBJECT "MyObject" | |
// Create & return MyObject instance to Lua | |
static int myobject_new(lua_State* L){ | |
double x = luaL_checknumber(L, 1); | |
*reinterpret_cast<MyObject**>(lua_newuserdata(L, sizeof(MyObject*))) = new MyObject(x); | |
luaL_setmetatable(L, LUA_MYOBJECT); | |
return 1; | |
} | |
// Free MyObject instance by Lua garbage collection | |
static int myobject_delete(lua_State* L){ | |
delete *reinterpret_cast<MyObject**>(lua_touserdata(L, 1)); | |
return 0; | |
} | |
// MyObject member functions in Lua | |
static int myobject_set(lua_State* L){ | |
(*reinterpret_cast<MyObject**>(luaL_checkudata(L, 1, LUA_MYOBJECT)))->set(luaL_checknumber(L, 2)); | |
return 0; | |
} | |
static int myobject_get(lua_State* L){ | |
lua_pushnumber(L, (*reinterpret_cast<MyObject**>(luaL_checkudata(L, 1, LUA_MYOBJECT)))->get()); | |
return 1; | |
} | |
// Register MyObject to Lua | |
static void register_myobject(lua_State* L){ | |
lua_register(L, LUA_MYOBJECT, myobject_new); | |
luaL_newmetatable(L, LUA_MYOBJECT); | |
lua_pushcfunction(L, myobject_delete); lua_setfield(L, -2, "__gc"); | |
lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); | |
lua_pushcfunction(L, myobject_set); lua_setfield(L, -2, "set"); | |
lua_pushcfunction(L, myobject_get); lua_setfield(L, -2, "get"); | |
lua_pop(L, 1); | |
} | |
// Program entry | |
int main(int argc, char** argv){ | |
if(argc > 1){ | |
lua_State* L = luaL_newstate(); | |
luaL_openlibs(L); | |
register_myobject(L); | |
if(luaL_dofile(L, argv[1])) | |
std::cerr << lua_tostring(L, -1); | |
lua_close(L); | |
}else | |
std::cerr << "Expected filename from command line!"; | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local obj = MyObject(42) | |
print(obj:get()) -- 42 | |
obj:set(-1.5) | |
print(obj:get()) -- -1.5 |
Also, you'll probably need this as well:
extern "C" { int luaopen_myobject(lua_State *L); }
Awesome! I got it!
Here's the code for anyone now or in the future:
First, you'll have to write your C++ file as it was at the top of this gist, but replace the main
function with this:
extern "C" {
// Program entry
int luaopen_myobject(lua_State *L)
{
luaL_openlibs(L);
register_myobject(L);
return 1;
}
}
You can compile using this Makefile
CC=g++
LUA_VERSION=5.4
output=myobject.so
build: *.cpp
$(CC) $< -g -llua -fPIC -shared -o $(output)
clean:
rm $(output)
When you run make
, that'll produce a myobject.so
.
Write a lua script like so. Call it test.lua
.
myobject = require "myobject"
local obj = MyObject(42)
print(obj:get()) -- 42
obj:set(-1.5)
print(obj:get()) -- -1.5
Finally, run it with lua test.lua
, and you should see the output
42.0
-1.5
Thanks @myQwil !!
Glad I could help :)
I'd like to add just a few things:
luaL_openlibs(L);
probably isn't necessary in this context- As far as I know, it's considered bad practice to use
lua_register
because it makes a global declaration. Instead, ourluaopen
function should deliver the library as a return value, which can then be made either local or global on the lua end. In our case, we can return a function like so:
static void register_myobject(lua_State* L) {
static const luaL_Reg meta[] = {
{ "__gc", myobject_delete },
{ NULL, NULL }
};
static const luaL_Reg meth[] = {
{ "set", myobject_set },
{ "get", myobject_get },
{ NULL, NULL }
};
luaL_newmetatable(L, LUA_MYOBJECT);
luaL_setfuncs(L, meta, 0);
luaL_newlib(L, meth);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
lua_pushcfunction(L, myobject_new);
}
and then in test.lua:
local myObj = require "myobject"
local obj = myObj(42)
...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
instead of using
int main(int argc, char** argv)
useint luaopen_myobject(lua_State *L)
lua is going to look for a function with that name