IupLua Advanced Guide

Exchanging "Ihandle*" between C and Lua

Each binding to a version of Lua uses different features of the language in order to implement IUP handles (Ihandle) in Lua. Therefore, functions have been created to help exchange references between Lua and C.

In C, to push an Ihandle in Lua's stack, use the function:

void iuplua_pushihandle(lua_State *L, Ihandle *ih);

In C, to receive an Ihandle in a C function called from Lua, just use one of the following code:

Ihandle* ih = *(Ihandle**)lua_touserdata(L, pos);

or using parameter checking:

Ihandle* iuplua_checkihandle(lua_State *L, int pos);

In Lua, if the handle is a user data create with the above structure, but not mapped to a Lua object, use the function:

iup.RegisterHandle(handle, classname)

where "classname" is the string returned in IupGetClassName.

In Lua, to access a handle created in C as a Lua object, alternatively use the function:

handle = iup.GetFromC(name)

where "name" is the name of the element previously defined with IupSetHandle.

Error Handling

In C to improve the error report, use the following functions to execute Lua code:

int iuplua_dofile(lua_State *L, const char *filename);
int iuplua_dostring(lua_State *L, const char *string, const char *chunk_name);
int iuplua_dobuffer(lua_State *L, const char *string, int len, const char *chunk_name); (since 3.15)

These functions mimics the implementation in the standalone interpreter for Lua 5, that displays the error message followed by the stack.

If iuplua_dofile fail to open the given file, then it will prepend the contents of the environment variable IUPLUA_DIR to the file name and tries to open it again. (Since 3.2)

If the these functions are used, the errors will be reported through the "iup._ERRORMESSAGE(msg)" function. By default _ERRORMESSAGE is defined to show a dialog with the error message. The global attribute "LUA_ERROR_LABEL" if defined will be used in a label inside the dialog (since 3.17).

In Lua, you can also use:

iup.dofile(filename: string) -> (values returned by the chunk)
iup.dostring(str: string) -> (values returned by the chunk)

But instead of returning the error code as in C, they return the values returned by the chunk. And they will still have the same error processing as the C equivalents. (since 3.17)

OBS: When printing an Ihandle reference the returned string is "IUP(type): address", for example "IUP(dialog): 08C55240".

The Architecture of IupLua 5

There are two important names in IupLua5: "iupHandle" and "iupWidget". (renamed in 3.15)

When you create an IUP element in Lua 5 it is created a table with a metatable called "iupWidget". This metatable has its "__index" method redefined so when an index is not defined it looks for it in the "parent" table. The table it self represents the class of the control. And all the classes inherit the implementation of the base class WIDGET. Each control class must implement the "createElement" method of the class. The WIDGET class also a member called "ihandle" that contains the Ihandle* in Lua. The constructor of the WIDGET class returns the handle. The purpose of these classes is to help the creation of the smart constructors in Lua, so instead of doing "ih = iup.Label("some")" we can do "ih = iup.label{title = "some"}". It also helps to define some methods for all elements like "ih:map()", "ih:show()" and others. The BOX class inherits from WIDGET and implements the construction using elements as parameters, along with some utilities like "ih:append(child)".

The Ihandle* is represented in Lua as a table with a metatable called "iupHandle". This metable has its "__index", "__newindex" and "__eq" methods redefined. The index methods are used to implement the set and get attribute facility. The handle knows its class because it is stored in its "parent" member.

Since the controls creation is done by the "iup.<control>" function, the application does not use the WIDGET class directly. All the time the application only uses the handle.

So, for example the IupLabel constructor works like this:

iup.label calls iup.LABEL:constructor
since iup.LABEL.parent = iup.WIDGET and iup.LABEL:constructor is not implemented
it calls iup.WIDGET:constructor  
then iup.WIDGET:constructor calls iup.LABEL:createElement    
and finally returns the created handle