-
-
Save Youka/2a6e69584672f7cb0331 to your computer and use it in GitHub Desktop.
| // 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; | |
| } |
| local obj = MyObject(42) | |
| print(obj:get()) -- 42 | |
| obj:set(-1.5) | |
| print(obj:get()) -- -1.5 |
How am I supposed to compile this? Can anyone offer a g++ command?
I'm using g++ myobject.cpp -llua -shared -fPIC -o myobject.so, but that's giving me this error when I run lua main.lua
lua: error loading module 'myobject' from file './myobject.so':
./myobject.so: undefined symbol: luaopen_myobject
stack traceback:
[C]: in ?
[C]: in function 'require'
main.lua:1: in main chunk
[C]: in ?
I'm trying to use Lua 5.4 if that helps.
Edit:
Oh, I see. This doesn't do what I need it to. You're supposed to compile this as a binary like so: g++ myobject.cpp -llua -o myobject then execute it like so: ./myobject test.lua. The C++ executes, loads up the Lua, then operates on it. Meanwhile I'm trying to do it the other way around. create a C++ library to be called from Lua. Specifically, I need a way to mutate an object from C++ (called from Lua) and then read by Lua.
Don't suppose anyone has an example for that? 😅
instead of using int main(int argc, char** argv) use int luaopen_myobject(lua_State *L)
lua is going to look for a function with that name
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_registerbecause it makes a global declaration. Instead, ourluaopenfunction 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)
...
Yes I created and registered the functions __add and __tostring, my question was more focused on memory Management and the garbage collection. When I create an object using lua and its passed with an add expression to the print function, another instance of the object is created in my __add implementation, but later I don't use that instance, because it was created on the print method. (I implemented the __gc method for delete all the created instances) Here is an example I am talking about (the __gc method prints the freed instances)
You can look more on my implementation in my repo. LAak interpreter
Also another question, there is a way to create a default __index method (e.g: x[0]) with multiple __index methods? I implemented __index methods for my vector object such as x:size() and x:normalize() but I cannot add the method for indexing without overriding the previous ones e.g: x[0] was changed to x:k(0) because __index is not associated with normalize and size.
Thank you