lua-users home
lua-l archive

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


D Burgess wrote:
>
> [...] the best way to manage (and mask) the
> difference between static and dynamically loaded libraries.

I played around with that already.  My attempt was like this:

lua.h defines a macro LUA_MODULE(module_name, start_func).
Every module (a static or dynamically loaded library) has one
instance of this macro in one of it's C-files, i.e.:

foo.c:
    LUA_MODULE("foo", foo_init)
    ...
    int foo_init(lua_State *L)
    {
        ...// a regular lua_CFunction to initialize the module
    }
    ...

Depending on whether the module should be compiled for static
or dynamic loading, the LUA_MODULE macro expands to different
(eventually system dependant) code.  The idea here is, that the
makefile specifies whether a library becomes static or dynamic.
The library source is always exactly the same.

I.e. for a dynamic shared library the macro expands to:

    lua_CFunction foo_init;
    lua_Dynlib lua_Dynlib_info = {
        LUA_DYNLIB_VERSION,
        sizeof(lua_Dynlib),
        sizeof(lua_Number), (lua_Number)LUA_DYNLIB_TESTNUMBER,
        "foo", foo_init
    };

The dynloader fetches the "lua_Dynlib_info" symbol, checks whether
the data in the struct-fields match the currently running inter-
preter, and, if everything's ok, pushes a cclosure for foo_init.


In the static case, the macro may become something like this:

    lua_CFunction foo_init;
    static void module_ctor(void) __attribute__((constructor));
    static void module_ctor(void)
    {
        lua_registermodule("foo", foo_init);
    }

The purpose of lua_registermodule is to collect a list of all
builtin modules.  The dynamic loader may search this list to
find a module before trying dlopen.

The above version requires compiler support for constructor functions
(called before main is entered).  If that is not available (or if one
does not want this magic), one could run a script from the makefile
to collect all LUA_MODULE statements of the selected sources and
create a helper c-file that may look like this:

    #define LUA_MODULE(name,func) lua_registermodule(name, func);
    static void register_lua_modules(void)
    {
        LUA_MODULE("foo", foo_init)
        LUA_MODULE("bar", bar_init)
    };

and which is linked to the executable together with the foo.o and
bar.o (the LUA_MODULE macro expands to nothing in foo and bar.o).
Then it's the application's duty to call register_lua_modules once
during startup.


[Btw, both versions (constructor and helper c-file) would allow
building a hybrid shared library consisting of multiple static
libraries.]


Ideally, all this functionallity is hidden inside loadfile.
loadfile first checks the registered modules table, then
searches on disk and decides whether the file is a dll/so,
a precompiled file, or a simple source.  Or, to make the
order selectable, one could add a magic directory (like
$builtin$) to the LUA_PATH variable to tell the loader
when to look into the module table, i.e.:

   $builtin$/?;/usr/lib/lua5/?.so;/usr/share/lua5/?.lua



The first point from the OP's posting (namespaces) could be
discussed independently.  It's usage policy.  I would prefer
fixed global names.  You have to manage the modulename name-
space anyway.  If you get collisions in the global variable
names, you're likely to get collisions in the module names
anyway.  If I say "to use this module you have to require'foo'"
I could also add "and then you can access it's functions via
the table Foo".  Besides, makes reading others sources easier
when each modules has a fixed name.  Else you may end up with
"str.toupper", "S.toupper" and "String.toupper".  Then, what
if a module (like i.e. "stdlib") wants to export multiple
tables?  As a last con from me: it's more complicated!  You have
to cache results of the first require call so that other
scripts can fetch the namespace table later.

But as I said, this can (and should) be discussed independently.

Ciao, ET.