Tuesday, May 3, 2011

Is there any way to programmatically determine in C/C++ how many parameters a Lua function expects?

Is there a way to determine how many parameters a Lua function takes just before calling it from C/C++ code?

I looked at lua_Debug and lua_getinfo but they don't appear to provide what I need.

It may seem a bit like I am going against the spirit of Lua but I really want to bullet proof the interface that I have between Lua and C++. When a C++ function is called from Lua code the interface verifies that Lua has supplied the correct number of arguments and the type of each argument is correct. If a problem is found with the arguments a lua_error is issued.

I'd like to have similar error checking the other way around. When C++ calls a Lua function it should at least check that the Lua function doesn't declare more parameters than are necessary.

From stackoverflow
  • What you're asking for isn't possible in Lua.

    You can define a Lua function with a set of arguments like this:

    function f(a, b, c)
       body
    end
    

    However, Lua imposes no restrictions on the number of arguments you pass to this function.

    This is valid:

    f(1,2,3,4,5)
    

    The extra parameters are ignored.

    This is also valid:

    f(1)
    

    The remaining arguments are assigned 'nil'.

    Finally, you can defined a function that takes a variable number of arguments:

    function f(a, ...)
    

    At which point you can pass any number of arguments to the function.

    See section 2.5.9 of the Lua reference manual.

    The best you can do here is to add checks to your Lua functions to verify you receive the arguments you expect.

    Ashley Davis : Well I did say I know what I am doing is not in the spirit of Lua :) With functions implemented and called entirely within Lua it will work the way you say, however what I want is a way to robustify the Lua/C++ interface. When you implement a C++ interface using Lua functions (using our custom Lua/C++ binding system) it makes sense to automatically enforce that the Lua functions that are being called from C++ implement the correct number of arguments. That said I suspect that it isn't possible to do what I want. Thought I'd ask all the same!
  • No, not within standard Lua. And is Aaron Saarela is saying, it is somewhat outside the spirit of Lua as I understand it. The Lua way would be to make sure that the function itself treats nil as a sensible default (or converts it to a sensible default with something like name = name or "Bruce" before its first use) or if there is no sensible default the function should either throw an error or return a failure (if not name then error"Name required" end is a common idiom for the former, and if not name then return nil, "name required" end is a common idiom for the latter). By making the Lua side responsible for its own argument checks, you get that benefit regardless of whether the function is called from Lua or C.

    That said, it is possible that your modules could maintain an attribute table indexed by function that contains the info you need to know. It would require maintenance, of course. It is also possible that MetaLua could be used to add some syntax sugar to create the table directly from function declarations at compile time. Before calling the Lua function, you would use it directly to look up any available attributes and use them to validate the call.

    If you are concerned about bullet-proofing, you might want to control the function environment to use some care with what (if any) globals are available to the Lua side, and use lua_pcall() rather than lua_call() so that you catch any thrown errors.

  • I wouldn't do this on the Lua side unless you're in full control of Lua code you're validating. It is rather common for Lua functions to ignore extra arguments simply by omitting them.

    One example is when we do not want to implement some methods, and use a stub function:

    function do_nothing() end
    
    full_api = {}
    function full_api:callback(a1, a2) print(a1, a2) end
    
    lazy_impl = {}
    lazy_impl.callback = do_nothing
    

    This allows to save typing (and a bit of performance) by reusing available functions.

    If you still want to do function argument validation, you have to statically analyze the code. One tool to do this is Metalua.

    Ashley Davis : Thanks. I am going to take a look at metalua. Static analysis of the Lua code might actually be better in my case than trying to do the checking at runtime.
    Alexander Gladysh : You're welcome. If you do so, please drop a line to Metalua's mailing list (http://lists.luaforge.net/mailman/listinfo/metalua-list). It is likely that you'll get some valuable advice from Metalua author.
  • The information you ask for is not available in all cases. For example, a Lua function might actually be implemented in C as a lua_CFunction. From Lua code there is no way to distinguish a pure Lua function from a lua_CFunction. And in the case of a lua_CFunction, the number of parameters is not exposed at all, since it's entirely dependent on the way the function is implemented.

    On the other hand, what you can do is provide a system for functions writers (be it in pure Lua or in C) to advertise how many parameters their functions expect. After creating the function (function f(a, b, c) end) they would simply pass it to a global function (register(f, 3)). You would then be able to retrieve that information from your C++ code, and if the function didn't advertise its parameters then fallback to what you have now. With such a system you could even advertise the type expected by the parameters.

    Ashley Davis : You made some good points. Although the system you propose in the 2nd paragraph is just as open to human error as the default case, eg in the normal case you have to rely on the coder to get the arguments right, in the second case the coder needs to get right both the arguments and the argument-metadata.

0 comments:

Post a Comment