[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Porting LuaCOM to Windows CE, and SWIG typemaps
- From: Don Hopkins <dhopkins@...>
- Date: Wed, 06 Sep 2006 13:03:20 -0700
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;
}