-
-
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_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)
...
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