How to register member function in lua without lua binding in C ++

I am using lua 5.1 in my C ++ game project, but I am having problems using lua when I try to register a C ++ member function. I want to use a member function of a C ++ class in lua, but the lua_register () function 3rd parameter can only accept a regular function pointer of type c or a static pointer to a member function.

I heard that the lua binding library can solve this problem, but I do not want to use lua bind. This is good, but too hard for my project. Is there a way to register a C ++ member function without any libraries? How can I do it?

+5
source share
2 answers

I myself have experienced this experience.

Basically there are two good solutions that I know. One of them is good if a member function for a class for which there will be only one element for each lua state. The other is more flexible, but more complex and slow. (I would like to learn other methods / improvements to these methods!)

I think lua_bind uses some templates very similar to method 1, but uses tricks to make the implementation flexible, like method 2. I think that any of them is more transparent than lua_bind does.

Method 1

(1) For each my_class member my_class that you want to pass to lua, it should take lua_State * L and return an int .

(2) During lua initialization, save the pointer to the corresponding my_class in lua_extraspace .

 *static_cast<my_class**>(lua_getextraspace(L_)) = &instance; 

(3) When you want to pass member functions to lua, use a template like this:

 typedef int (my_class::*mem_func)(lua_State * L); // This template wraps a member function into a C-style "free" function compatible with lua. template <mem_func func> int dispatch(lua_State * L) { my_class * ptr = *static_cast<my_class**>(lua_getextraspace(L)); return ((*ptr).*func)(L); } 

Then you register things like this:

 const luaL_Reg regs[] = { { "callback_1", &dispatch<&my_class::callback_1> }, { "callback_2", &dispatch<&my_class::callback_2> }, { "callback_3", &dispatch<&my_class::callback_3> }, { NULL, NULL } }; luaL_register(L, regs); 

Method 1 has the advantage that it is quite simple and very fast, I think it will be faster than lua binding. Because get_extraspace does nothing but a little pointer arithmetic. Most likely, a good compiler can optimize the dispatch pattern so that the function it executes is built-in and there is no overhead.

You might want to modify the dispatch pattern so that a null pointer is specified in the extrasphere, depending on how the lifetimes of your lua state and your my_class .

Potentially, you can also store more complex things in the extrasphere, for example, pointers to several different objects or even as a vector or something like that (you can read about how to configure lavas extraspace in your documents). Or you can store things in the lua registry, and the send function can retrieve them from there, but the extra space is faster - it is up to you.

Method 2

In method 2, you mainly use the usual lua methods to push the C ++ object to lua that belongs to lua, but you do it where the object is C ++ std::function and you overload metafunction _call so that it calls function. (If you are not in C ++ 11, you can use boost::function .)

Then, when you want to push a C ++ member function to lua, you use std::bind to make it a function object.

This method has the disadvantage that inside lua the type of the “function” will actually be userdata , but since you can call it just fine and use it as a function, it doesn't really matter. If this is bad for you, one thing you can do is use the same trick, but also then do a closure that has the userdata object of the object as an upvalue, and when the call is called, it just redirects the arguments to and returns results. Then the closure type will be function in lua, but it will do basically the same thing.

 typedef std::function<int(lua_State *)> lua_function; char const * cpp_function = "CPP_Function"; static int intf_dispatcher ( lua_State* L ) { //make a temporary copy, in case lua_remove(L,1) might cause lua to garbage collect and destroy it lua_function f = * static_cast<lua_function *> (luaL_checkudata(L, 1, cpp_function)); // remove from the stack before executing, so that like all other callbacks, f finds only its intended arguments on the stack. lua_remove(L,1); int result = (f)(L); return result; } static int intf_cleanup ( lua_State* L ) { lua_function * d = static_cast< lua_function *> (luaL_testudata(L, 1, cpp_function)); if (d == NULL) { std::cerr << "ERROR: intf_cleanup called on data of type: " << lua_typename( L, lua_type( L, 1 ) ) << std::endl; lua_pushstring(L, "C++ function object garbage collection failure"); lua_error(L); } else { d->~lua_function(); } return 0; } static int intf_tostring( lua_State* L ) { lua_function * d = static_cast< lua_function *> (luaL_checkudata(L, 1, cpp_function)); // d is not null, if it was null then checkudata raised a lua error and a longjump was executed. std::stringstream result; result << "c++ function: " << std::hex << d; lua_pushstring(L, result.str().c_str()); return 1; } void register_metatable ( lua_State* L ) { luaL_newmetatable(L, cpp_function); lua_pushcfunction(L, intf_dispatcher); lua_setfield(L, -2, "__call"); lua_pushcfunction(L, intf_cleanup); lua_setfield(L, -2, "__gc"); lua_pushcfunction(L, intf_tostring); lua_setfield(L, -2, "__tostring"); lua_pushvalue(L, -1); //make a copy of this table, set it to be its own __index table lua_setfield(L, -2, "__index"); lua_pop(L, 1); } void push_function( lua_State* L, const lua_function & f ) { void * p = lua_newuserdata(L, sizeof(lua_function)); luaL_setmetatable(L, cpp_function); new (p) lua_function(f); } 
+6
source

The previous answer is correct.

In terms of the question, does it depend when you call a class member from lua if you want lua to solve the used object, or you want to bind it to an instance member of a fixed object. If it is fixed, it is quite difficult to use std :: bind to create a function object that contains a pointer to a member function to call and this pointer to call it, and register this with lua.

http://en.cppreference.com/w/cpp/utility/functional/bind

The code will look like this:

 class C { public: void blah(lua_State* L); }; C inst; lua_pushcclosure(L, std::bind(&C::blah, &inst, std::placeholder::_1), 0); lua_setglobal(L, "blah"); 

Also for a more natural way of associating C / C ++ functions with lua, which avoids writing wrappers, look here:

http://inspired-code.blogspot.com.au/p/automagical-language-bindings-using-c.html

-2
source

All Articles