lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Noel Frankinet wrote:
Hello Don,
I'm in the middle of the creation of a full GUI-LUA framework (no name yet, something like GLUA?) for win32 and WCE.
Its WTL based to be as small as possible.
I suppose that I will try to host activeX in the near future, so LUACOM will come handy.
I'm ready to help for the windowsCE porting
Best wishes

Hello, Noel -- good to hear from you!
That sounds excellent -- I am using WTL myself. It's a lot better than MFC!
I would love to be able to host IE and the Flash player via ActiveX, on the Pocket PC, and LuaCom will make that possible. Please keep me abreast of your progress, and I'll let you know how the LuaCom porting goes.

Something I'm currently puzzling with is how best to do callbacks from C++ to Lua elegantly and efficiently. I want to have a lot of callbacks that the C++ code will make to Lua, so objects are easy to hook and customize. I'd like for Lua scripters to be able to define handlers in classes (prototypes), as well as overriding them on individual objects. It would be nice to create a C++ object from Lua, and then bonk its wrapper table with setmetatable, so it inherits from a user-scripted prototype, and picks up a bunch of methods that tailor its behavior beyond what the C++ object knows how to do. But also I want to be able to set handlers on individual Lua objects, too.

I'm wondering if there's a nice lazy efficient way to do callbacks by putting named functions in Lua tables (so the C++ code can issue a callback if a handler is defined, or do nothing if not), instead of defining a bunch of callback reference members in C++ objects, each of which must have its own get/set wrappers and Lua object reference.

My C++ objects have a reference to the Lua object that wraps them. I wrote a SWIG typemap that whenever it returns a C++ object, it makes a Lua wrapper, and stashes a reference to the wrapper in the C++ object as an integer, so callbacks can pass that same wrapper to the Lua function as the first argument, so they know which object the callback is happening on, and they don't get a different wrapper every time, so they can stash their own data in the wrapper object, that will persist across callbacks. (Like the mouse down handler remembering the (X, Y, time) location where the mouse went down, and the mouse move and mouse up handlers checking it to detect a drag or click -- without the C++ code having to know about it, or having any member slots to store the Lua-only instance variables.)

My first attempt at callbacks uses Lua references in C++ member variables, which the C++ code calls back to. But it requires a C++ member slot and lot of wrapper code to be generated for each callback, increasing the size of the C++ object and code, and it's not as flexible, because it requires changing several places in the code to add a new callback. I'd like adding new callbacks to be as trivial as calling obj->callback("onNewCallbackIJustMadeUp"), by using Lua tables to store the callbacks instead of C++ members, so if the callback isn't defined, nothing happens, and no memory is wasted.

Since I'm using the object reference in the C++ object to maintain the same Lua wrapper, it should be possible to put per-instance callbacks in that wrapper. But the thing I'm wondering about (because I don't know Lua well enough), is how can I set the metatable of the C++ object wrapper to some user defined class from Lua, without losing the original metatable that binds it to the C++ object? Can a Lua object have two metatables (kind of like the way Self lets you inherit from several parents at once)? Or can I define a "grandparent" member in my C++ object or slot in the wrapper object to hold a secondary metatable that my C++ code delegates to if it doesn't define a slot?

When you call a callback, the Lua wrapper should be first in the inheritance chain, then the C++ object, then the Lua prototype.

I'd like to learn more about the best practices for making C++ extensions to Lua that delegate back to Lua objects and prototypes, please!

Thanks a lot, and have fun!

   -Don

PS: Here's the SWIG typemap, and a SWIG macro that define typemaps for the C++ objects for the "obj' accessors, that automatically glue the C++ object and dthe Lua wrapper table together. You use the macro by going "%def_lua_obj_typemap(Menu)" for each C++ class (like "Menu") you want to bind. Following the SWIG macros is an example of the wrapper code it generates, to create a new menu, and initialize the "obj" member of the "Menu" class.

////////////////////////////////////////////////////////////////////////
// Typemaps for storing integer LuaRef's in members, that let
// you get and set Lua objects from the scripting language.
// Uses Lua references to store integer references to Lua
// objects in C++ objects. If the reference we're setting
// already refers to an object, then we unreference the
// old reference so it gets garbage collected.
//
// Note: You should declare the following typedef:
// typedef int LuaRef;
//
// Note: The C++ object's destructor should clean up
// all its references, by calling LuaUnRef(&obj).


%typemap(in)
   LuaRef
%{
   // Make a reference to the value.
   lua_pushvalue(L, $input);
   $1 = luaL_ref(L, LUA_REGISTRYINDEX);
%}

%typemap(memberin)
   LuaRef
%{
   // If there's a valid reference there already,
   // then unreference it so it gets gc'ed.
   if (($1 != LUA_NOREF) &&
       ($1 != LUA_REFNIL)) {
       luaL_unref(L, LUA_REGISTRYINDEX, $1);
   }
   // Now set the reference.
   $1 = $input;
%}

%typemap(out)
   LuaRef
%{
   // Return the value of a reference.
   if (($1 == LUA_NOREF) ||
       ($1 == LUA_REFNIL)) {
       lua_pushnil(L);
   } else {
       lua_rawgeti(L, LUA_REGISTRYINDEX, $1);
   }
   SWIG_arg++;
%}


////////////////////////////////////////////////////////////////////////
// Macro for defining typemaps for returning C++ objects to Lua,
// which have a Lua object reference member called "obj", that
// stores the original Lua wrapper table. The first time we return
// the C++ object, we make a Lua object, and store a reference to
// it in the C++ object's "obj" member. Subsequently we check the
// C++ object's "obj" field, and return the same Lua object wrapper,
// instead of creating a new one each time.
//
// Note: The C++ object's destructor should clean up
// all its references, by calling LuaUnRef(&obj).


%define %def_lua_obj_typemap(CLASS)
   %typemap(out)
       CLASS *
   %{
       // Return the value of a CLASS, caching the Lua object in obj.
       if ($1 == NULL) {
           lua_pushnil(L);
       } else if (($1->obj == LUA_NOREF) ||
                  ($1->obj == LUA_REFNIL)) {
           SWIG_NewPointerObj(L,$1,SWIGTYPE_p_ ## CLASS,0);
           lua_pushvalue(L, -1);
           $1->obj = luaL_ref(L, LUA_REGISTRYINDEX);
       } else {
           lua_rawgeti(L, LUA_REGISTRYINDEX, $1->obj);
       }
       SWIG_arg++;
   %}
%enddef


Here is the wrapper for the "new Menu" function, that stashes a reference to the Lua wrapper object it made in the "obj" field of the new menu.

static int _wrap_new_Menu(lua_State* L) {
 int SWIG_arg = -1;
 Menu *result = 0 ;

 result = (Menu *)new Menu();
 SWIG_arg=0;

 // Return the value of a Menu, caching the Lua object in obj.
 if (result == NULL) {
   lua_pushnil(L);
 } else if ((result->obj == LUA_NOREF) ||
   (result->obj == LUA_REFNIL)) {
   SWIG_NewPointerObj(L,result,SWIGTYPE_p_Menu,0);
   lua_pushvalue(L, -1);
   result->obj = luaL_ref(L, LUA_REGISTRYINDEX);
 } else {
   lua_rawgeti(L, LUA_REGISTRYINDEX, result->obj);
 }
 SWIG_arg++;

 return SWIG_arg;

fail:
 lua_error(L);
 return SWIG_arg;
}


Here is the wrapper for the Menu class variable "curMenu" accessor, which returns the current menu, returning the same "obj" Lua wrapper that was made when it was wrapped:

static int _wrap_Menu_curMenu_get(lua_State* L) {
 int SWIG_arg = -1;
 Menu *result = 0 ;

 result = (Menu *)Menu::curMenu;
 SWIG_arg=0;

 // Return the value of a Menu, caching the Lua object in obj.
 if (result == NULL) {
   lua_pushnil(L);
 } else if ((result->obj == LUA_NOREF) ||
   (result->obj == LUA_REFNIL)) {
   SWIG_NewPointerObj(L,result,SWIGTYPE_p_Menu,0);
   lua_pushvalue(L, -1);
   result->obj = luaL_ref(L, LUA_REGISTRYINDEX);
 } else {
   lua_rawgeti(L, LUA_REGISTRYINDEX, result->obj);
 }
 SWIG_arg++;

 return SWIG_arg;

fail:
 lua_error(L);
 return SWIG_arg;
}