tags:

views:

144

answers:

3

I have an application that uses lua files for some of its more obscure configuration options. As such it mostly contains calls into the app to create things and alter properties; most C functions don't have a return value but some do.

I now have a need to read these same configuration files into a different application, and perform significantly different things when the functions are called (so I can't use common code). In addition, I'm only interested in a subset of the possible functions, and I think I can get away with by default ignoring (and/or returning nil) any other function call.

So I'm wondering what the best approach is here. How (from a C++ app), can I load and execute a lua script such that expressions etc are evaluated as normal but I can intercept and process certain app-defined C functions while simply ignoring (returning nil if required) calls to any other C functions?

(Note: I do have access to the vocabulary of the original app, which mostly uses luabind; I could just use the same definitions and change the implementation, but that's too fragile since the original app can have more functions added to it later. I would like something more generic.)


The goal is to get a bit of C code which I can use as a generic placeholder; the end result being "anything that's defined (standard library routines, functions defined in Lua, and C functions explicitly registered), call it as normal; for anything else, call one specific routine that simply does nothing, instead of raising an error". And preferably something compatible with luabind.

The whole process is initiated by a bit of C code that sets up the Lua environment, loads a set of files, calls one function, and then destroys the environment. There won't be anything ongoing.

A: 

If you have some way of differentiating the C functions from the ordinary Lua functions in your script, you can iterate over all functions defined in your Lua system, and for each C function that isn't on the list of functions you care about, replace that function's implementation with a simple nil-returning one. This is really easy if all the c-binding functions are defined tidily together in a table; if they're in _G you have a bit of a job ahead of you. The functions you do care about get bound via luabind as normal.

David Seiler
Some are global and some are in tables (and some are in sub-tables). In this case I'm happy with assuming no errors, so basically anything that is already defined as a function must be a lua function and anything that is unknown might be either a C function or a table name.I do know the names of the tables and the functions that currently exist, but I would *like* to not rely on that knowledge, since the original app is extensible.
Miral
+2  A: 

How about using setfenv and a metatable? You can replace global environment table of certain function with an empty table. And then, set the placeholder function to ignore C-defined function.

local env = {}  -- empty environment
local metatbl = {}
function metatbl.__index (tbl, key)  -- provides placeholder function
    return function()
        print(key .. " called")
        return(nil)
    end
end

setmetatable(env, metatbl)

function samplefunc()  -- your Lua code goes here
    globalfunction "xyz"   -- calls placeholder function
end

setfenv(samplefunc, env)
samplefunc()

If you want to use build-in function such as print, you can push it into env talbe like:

local env = {print = print}
torus
Sorry, possibly I didn't explain this properly. I want all the standard Lua library functions to still work as normal (though having said that, it would be useful to ignore certain ones such as the file i/o functions). And I don't want to write more lua code; the replacement functions are replacing C functions and should themselves be written in C, and the code to set things up should also be in C.
Miral
@Miral: Your issue with the standard library functions was covered at the end of this answer. Also, anything written in Lua can also be written as C (even though it will be more difficult). In addition, luaL_dostring() could be used to load similar Lua code from within C.
gwell
@gwell: Kind of, except that requires each thing I want to work to be added individually, which isn't that great.I tried a variation on this which set env to getfenv(0) instead of {}. This worked ok for global functions but not those in subtables, and gave a fairly obscure (to the user) error in that case.
Miral
+3  A: 

Set a __call metamethod for nil:

debug.setmetatable(nil, { __call=function () end })

The _index metamethod for _G or other tables does not work because the name is resolved before the call.

(You don't need to use the debug API or library if you're setting this from C.)

lhf
I've ticked this one (for now) because it seems to work a bit better than the other options thus far, although it did require registering the tables/subtables containing functions in advance as well. (Which is ok, but it'd be nice if this could be dealt with automatically too.)
Miral
I'm not sure what you mean, but you can add __index to the table above:debug.setmetatable(nil, { __call=function () end, __index=function() end })This will make a.b return nil if a is not defined.
lhf
Apologies for the delay, but I've been working on other projects for a while. Using __index as well seems to do what I want, thanks!
Miral