Any integration code that doesn’t use an ad-hoc helper library looks like assembly listing (not even in disguise, cause it basically is bytecode but in C).
See my other reply: https://news.ycombinator.com/item?id=42519033
> Any integration code that doesn’t use an ad-hoc helper library
There are mature bindings for most languages. You only need to deal with the C API directly when you
1. Create a new language binding
2. Write a Lua extension
3. Call Lua from C
Typically, Lua is embedded as a scripting or configuration language into another project, and in this case you wouldn't even be aware of the C API.
(from elsewhere) It is a low-level API for creating bindings to other languages and as such typically isn't used directly. (Well, unless you are writing C :)
Yeah, unless. But even under this assumption, wrapping handles to values would be much more easier in a language-specific wrapper, because it wouldn’t have to watch out for stack overflows etc and solve the exploding stack problem in general in naive api-user-side loops. This thread started as Lua stack API is better than Python’s, iirc. But Python API already looks like a high-level wrapper doing exactly that, so… what was the point again?
I’m a little concerned that everyone talks high matters here without providing examples or at least looking into “final particles”. Cause that’s where claims get tested against reality. I spent too many years in deep Lua and its mailing list to miss something obvious that everyone knows.
I would agree that the Lua C API itself is not very eegonomic, but the wrappers/bindings it enables absolutely are! For example, integrating Lua in C++ with sol2 is an absolute joy.
I understand how gcs work and their nuances. My only problem was with the "fantastic" part. Lua stack is not "not ergonomic", but absolutely shit to work with, and "there's wrappers" is not an argument, cause they are not a part of distribution but external projects ought to fix the mess.
This would only work when being called from Lua, but not when the host calls into Lua (on the top level).
I do not do a ton of low level development, but my impression is that you do not want a demanding interface. You want an interface that is straightforward, doesn't have complex pre & post conditions, and doesn't crash. Leaking 4 bytes per frame is bad but crashing would be worse! You may think you want a library that refuses to function at all unless its called perfectly, but once you're in the trenches you may change your mind.
To my mind, it's a very good sign when there are popular "adapter" libraries. It means the underlying code (Lua) can be used many ways and people have found best practices for different approaches. When I used it I was not working on a game and it would have been worse for us if Lua's interface was "built for games." Being very bare bones and low level allows flexibility and that flexibility ensures a healthy ecosystem.
#define T lua_gettop(L)
…
int t = T;
…
assert(T == t + expected_inc);
…
lua_pushvalue(L, -2); // note: this is b
lua_remove(L, -3); // old b
// eventual realization that
// -index is fragile
// and rewrite to
int start = T;
int a = ++start;
int b = ++start;
luaL_checkstack(L, 10, "…");
…
lua_pushvalue(L, b);
lua_remove(L, b);
lua_call(L, …);
luaL_ref(L, …);
…
lua_settop(L, t - 1);
If you have ever done this, idk how that feels flexibility. I’d rather get handles to a and b and directly call a function with them as like: LFN *func = LGETFN(L, LARG(1));
LTAB *retvs = LCHKTAB(
LCALL(L, func, 3,
LGET(L, LARG(2)),
LGET(L, LARG(3)),
LGET(L, LUPV(1))
)
);
me.retvs = LREF(L, retvs);
// everything but ref’d
// things disappears here:
return;
And avoid all this stack-in-your-mind and stack-in-your-int nonsense, which is completely unnecessary in C and I fail to see where exactly it is flexible, compared to the short snippet above. Stack is just how vm sees it, it is not any special “for you” thing, it’s literally implementation shining through.I’d like to see a specific example (low-key, I left Lua) on how its stack provides more flexibility or anything, because in my humble experience it does not, absolutely, do anything like it. It just makes you play these off-by-one games all day for no good reason.
You can, ofc, write your Lua wrapper like your example - all in one place. It reduces the risk of misuse. But you could also do it differently - which is the flexibility that I think is a strength.
What I find fantastic about the stack-based API is that it exposes very few interpreter internals. As a result the authors may drastically change the implementation without breaking the public API. Most importantly, they hide the internal object representation and as a user you only ever deal with plain C types, like integers, floats, C-strings and user data pointers. As a consequence, you don't have to think about memory management because all types are passed by value! In fact, someone could theoretically write a Lua implementation with reference counting and the API wouldn't have to change. It really is a very smart and elegant design.