This section finally describes the implementation details and how modules
should us the Lua interface. First, in section 3.1
we look at types supported by the interface, how objects are passed to Lua
code and how Lua tables should be accessed from Ion and modules. In section
3.2 the methods for exporting functions and how they
are called from Lua are explained and in section 3.3 the
method for calling Lua functions is explained.
The following types are supported in passing parameters between the
C side of Ion and Lua:
Identifier
character
C type
Description
i
int
Integer
s
char*
String
S
const char*
Constant string
d
double
b
bool
t
ExtlTab
Reference to Lua table
f
ExltFn
Reference to Lua function.
o
Any WObj*
The difference between identifiers 's' and 'S' is that constant
strings as return values are not free'd by the level 1 call handler
(see below) after passing to Lua (lua_pushstring always makes a
copy) unlike normal strings. String parameters are always assumed to be
the property of the caller and thus implicitly const.
Likewise, if a reference to 't' or 'f' is wished to be stored
beyond the lifetime of a function receiving such as an argument, a new
reference should be created with extl_ref_table/fn.
References can be free'd with
extl_unref_table/fn. References gotten as return values with
the extl_table_get (how these work should be self-explanatory!)
functions are property of the caller and should be unreferenced with the
above-mentioned functions when no longer needed.
The functions extl_fn/table_none()
return the equivalent of NULL.
WObjs are passed to Lua code with WWatch userdatas pointing to
them so the objects can be safely deleted although Lua code might still be
referencing them. (This is why SWIG or tolua would not have helped in
creating the interface: extra wrappers for each function would still
have been needed to nicely integrate into Ion's object system. Even in
the case that Ion was written in C++ this would be so unless extra bloat
adding pointer-like objects were used everywhere instead of pointers.)
It may be sometimes necessary check in Lua code that a value known to
be an Ion WObj is of certain type. This can be accomplished with
obj_is(obj, "typename"). obj_typename(obj) returns type
name for a WObj.
Exported functions (those available to the extension language) are
defined by placing EXTL_EXPORT before the function implementation
in the C source. The script mkexports.pl is then used to automatically
generate exports.c from the source files if
MAKE_EXPORTS=modulename
is specified in the Makefile. All pointers with type beginning with a 'W'
are assumed to be pointers to something inheriting WObj. In
addition to a table of exported functions and second level call handlers
for these, exports.c will contain two functions
module_register_exports() and
module_unregister_exports() that should then be called in module
initialisation and deinitialisation code.
You've seen the terms level 1 and 2 call handler mentioned above.
The Lua support code uses two so called call handlers to convert and check
the types of parameters passed from Lua to C and back to Lua. The first
one of these call handlers is the same for all exported functions and
indeed lua sees all exported as the same C function (the L1 call handler)
but with different upvalues passing a structure describing the actual
function and the second level call handler. The L1 call handler checks
that the parameters received from Lua match a template given as a string
of the identifier characters defined above. If everything checks out ok,
the parameters are then put in an array of C unions that can contain
anyof these known types and the L2 call handler is called.
The L2 call handler (which is automatically generated by the mkexports.pl
script) for each exported function checks that the passed WObjs
are of the more refined type required by the function and then calls the
actual function. While the WObj checking could be done in the L1 handler
too, the L2 call handlers are needed because we may not know how the target
platform passes each parameter type to the called function. Thefore we
must let the C compiler generate the code to convert from a simple and
known enough parameter passing method (the unions) to the actual
parameter passing method. When the called function returns everything
is done in reverse order for return values (only one return value is
supported by the generated L2 call handlers).
The functions
extl_call,
extl_call_named,
extl_dofile and
extl_dostring
call a referenced function (ExtlFn), named function, execute a
string and a file, respectively. The rest of the parameters for all these
functions are similar. The 'spec' argument is a string of identifier
characters (see above) describing the parameters to be passed. These
parameters follow after 'rspec'. For dofile and dostring these parameters
are passed in the global table arg (same as used for program command
lien parameters) and for functions as you might expect. The parameter
'rspec' is a similar description of return values. Pointers to variables
that should be set to the return values follow after the input values.
The return value of all these functions tells if the call and parameter
passing succeeded or not.
Sometimes it is necessary to block calls to all but a limited set of
Ion functions. This can be accomplished with
extl_set_safelist.
The parameter to this function is a NULL-terminated array of strings
and the return value is a similar old safelist.
The call extl_set_safelist(NULL) removes any safelist and allows
calls to all exported functions.
Configuration files should be read as before with the function
read_config_for
except that the list of known options is no longer present.
Winprops are now stored in Lua tables and can contain arbitrary
properties. The 'proptab' entry in each WClientWin is a reference
to a winprop table or extl_table_none() if such does not exist
and properties may be read with the extl_table_gets functions.
(It is perfectly legal to pass extl_table_none() references to
extl_table_get*.)