lua-users home
lua-l archive

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


On Mon, Jul 11, 2016 at 03:45:15PM -0400, ThePhD wrote:
> Dear William,
<snip>
>      The reason this came up for me was because I was using some magic to
> basically count the number of arguments in a C function and then auto-wrap
> it. Problem is, certain types can be "left out" (e.g., a pointer is
> automatically left out and becomes nil/nullptr), and while the getters for
> lua types using "lua_to{x}" worked just fine with some type checking, the
> magic stuff would still try to pop exactly N arguments, even if only a few
> were left on the stack. I guess I could just do a `min(arg_count,
> lua_gettop(L))` so I don't over-pop the stack, and so I can handle the case
> of coroutines nicely too?

It's hard to say without knowing exactly what you're trying to solve. But if
you don't have have some kind of loop in the C function(s), then you
wouldn't need to worry about coroutines.

FWIW, I think the learned experience of many regulars here is to avoid magic
functions, wrappers, and binding generators. I've worked extensively with
Perl XS, which is almost entirely magic wrappers and generators around an
underspecified C interface. Other languages are similar, though not quite as
bad. I've run into more trouble trying to simplify Lua's C API or trying to
automate bindings than just accepting a little redundancy. The Lua VM and
language semantics were designed to make the C API and C modules first-class
citizens. That's unique among scripting languages--languages like Python and
JavaScript cannot add Lua-style asymmetric coroutines, for example, because
both the external and internal APIs are implicitly bound to the C stack. And
that's because they were too concerned too early in the design cycle with
convenience--you can prematurely optimize for "convenient" APIs just as you
can prematurely optimize for performance. If you try to be too clever too
early, you'll find that you've coded yourself into a corner, and your future
feature and performance improvements will be dictated by prior bad
decisions.

Along those lines, another piece of advice commonly heard on this list: when
binding C or C++ code, it's often better to keep the bindings relatively
simple with 1:1 mappings. Then wrap those bindings with Lua code to make the
module API more Lua-like. So for big modules you'll often see the C bindings
in a module named foo.core (foo/core.so), which is loaded and wrapped by a
Lua-script module, foo (foo.lua).

For those cases where you do need to add some magic to the C bindings to
make the module work well (like yielding at the C level, creating C
closures, specialized ownership patterns, etc), binding generators will
often get in the way. They make the easy stuff easier, but the difficult
stuff more difficult or impossible. That's not a good trade-off. Direct use
of the Lua API is usually the best balance, IME.