LuaBridge
LuaBridge copied to clipboard
Primitive type references as return values/arguments of member functions
I tried something along the lines of:
template<class T>
void lua_register_vector(lua_State *L, std::string name)
{
using namespace luabridge;
T& (std::vector<T>::*at_ptr)(size_t) = &std::vector<T>::at;
getGlobalNamespace (L)
.beginNamespace ("std_vector")
.beginClass < std::vector<T> > (name.c_str())
.addProperty ("size", &std::vector<T>::size)
.addFunction ("at", at_ptr)
.endClass ()
.endNamespace ();
}
If T is a primitive type, calling :at on such a vector in lua results in assertions like:
"LuaBridge/LuaBridge.h:2711: static void luabridge::Detail::UserdataPtr::push(lua_State_, void_, const void*): Assertion `(lua_type(L, (-1)) == 5)' failed." (note how the line number refers to a line with an assertion but not the one that has lua_type in its call... weird)
It seems to work perfectly fine when T is a registered other class though. I get the same behaviour from trying to register methods that take references to primitive types as arguments.
From quickly skipping though the code I get the impression this is connected with Stack<T> using the T& specialization in these cases while assuming T is a registered class, which the primitive types aren't?
This has to do with "shared lifetime". The Stack is specialized to detect template classes with one argument, and assumes that they represent a container. My abilities with templates are not particularly spectacular, and I've always suspected that the specializations I am using to detect the shared lifetime containers would cause problems later. I didn't know exactly what those problems would be but I think you just stumbled on it.
If you can think of a better way to detect shared lifetime containers that doesn't get confused by classes with single template arguments, I'm all ears.
I'm not entirely competent with lua but the way it looks to me it doesn't have a concept of reference to number anyway, right? So preserving the "referenceness" of those arguments would require some sort of reference to primitive wrapper within lua or so. For my example up there you won't be able to use "vec:at(0) = 42" anyway. A cheap way to "fix" the issue is to simply copy paste the primitive Stack specialization and just also specialize them for references? It makes my example work but I'm unsure about the implications in regards to expected behavior. For example a function that takes a int& that is called from lua won't actually change the value of the passed lua variable.
Hmm...this problem is not what I thought it was. You might be right about adding specializations for references to primitive types. I will have to think about this. I think that the proper behavior in this case would be to generate a static compile-time assertion, because like you said the int& won't actually be changed since Lua doesn't have references to numbers.
I guess it would be reasonable to have working specializations for const T& and emit a static assert for T&. The reason being that const T& is often used in place of pass by value for optimization purposes but the intent is usually the same (people that const_cast get rightfully punished ;) ). The same probably also applies to the pointer case, I didn't try that though.
Edit: A similar issue appears with const references to container objects. It only tests for "containerness" if the container is passed by value but not if passed by const reference.
For reference I commited my hacky workarounds/fixes here: https://github.com/progschj/LuaBridge/commit/1243e1b1d0b3fcbab2133a16e95bb58dc412a975 they are mostly untested, badly commented and very likely incomplete.
I left a note, you should try working in the develop branch, since it has about 6 months worth of improvements and will be released very soon.
I created a slightly cleaner version on the develop branch that only handles the const& cases. I'm unsure if const* should also work similar or if just all the other flavors of pass by reference should just give static asserts?
https://github.com/progschj/LuaBridge/commit/06f36402a42915b1d59df07cb1fce470ebe1f616
Hmm.... const* should generate a static assert because there's no equivalent in Lua. What meaning would it have?
Not sure, maybe there are people that prefer const pointers over const references for being more explicit on the caller side? For my purposes having const& working and all other versions assert seems fine. Is there some preferred method for the static assert already in the code (I haven't found one if there is)?
There's no "official" static assert, the technique I use is to make the member function private in the specialized class and leave behind a descriptive comment above the declaration explaining why it is disallowed.