";
luaL_dostring(L, code);
The way I handled it at first with the "binding magic" was this:1. check if the pointer is null
2a. Yes -> push Lua's nil with `lua_pushnil(L)` (what sol::nil represents in C++ land)
2b. No -> push userdata
With this way, doing a check for "v == nil" works and the code prints "true". But, when "receiving_function" is called and it inspects "v"s type, there's a bit of a problem. What's being asked for is a reference to a std::shared_ptr. This does not work because lua_touserdata returns `nullptr`, and thusly there's no std::shared_ptr inside I can pass a reference to. The type checker
did fail this code. Users complained that this code should "still work", because it violated their expectations. Someone filed an issue, where I gave them a tiny amount of advice to just take a raw pointer T*, where I could convert Lua's nil to a "nullptr" and not have to find a "std::shared_ptr" inside of the userdata.
Still, I tried a second way to handle it with "binding magic":1. push userdata of std::shared_ptr, regardless of whether it contained a nullptr or not
With this way, doing a check for "v == nil" does not work and prints "false". This violated user expectations and once more an issue was filed with my library. When "receiving_function" is called and it inspects "v"s type, it works because there's a userdata, and thusly I can hand the user a reference to the std::shared_ptr, even if that std::shared_ptr is just a container for "nullptr". Note: A fringe benefit of doing it the secondary way is if these userdata are put into a table as a "sequence", then there are no "nil" holes. (This does not affect collections and data structures returned from C++, as we override the metatable for them and let Lua 5.2/5.3 proper iterator or pairs functions, where we use the well-designed Lua iteration hooks to provide safe iteration over the entire sequence or data structure regardless of what it contains.)
"But why does someone need a reference to the std::shared_ptr and not just a value?"When we store non-primitive types in Lua with our "binding magic" (sol2), they are stored as userdata of some sort. All userdata carry with it the expectation that you can take a reference to the original data that you pushed: this allows people to do things like work with "std::unique_ptr"s and other types which cannot be copied (and thusly, we cannot take them by-value in a function because "by-value" in a function signature means "take a copy"). It also allows someone to directly manipulate the data held in Lua, without needing to perform a special dance or copy, swap or move their data (all operations can be done in-place on Lua's memory). I also cannot specialize the behavior for checking if someone takes a shared_ptr by-value versus if they ask for a reference: I always have to return a reference from the function that retrieves a specific type.
"What about writing a null-checking function?"
I have spent a long-time curating the Binding Magic (sol2) Library's documentation. Nobody will read it unless something goes wrong, and even then I need to find a way to allow them to quickly find out that working with sol2's types would require me to find the perfect spots to place this information so nobody makes any mistakes. I want the library to provide good defaults that make the most sense, which is why I'm trying these ways and exploring options for handling it. If I write a null-checking function, I also need to make it opt-in. I have a mechanism for including "built-in" libraries, and I could very well write a "built in" lib that comes with my framework where people have to use "sol.is_null( some_userdata )" to check. This, introduces a piece of sol2-specific code into people's code-bases and inevitably ties them to me. The goal of this library is that all of the code looks and feels like Lua code, with no special library-internal libs or strings attached. The Lua code is supposed to look like plain old Lua code, and be replaceable with Lua metatables and other stuff so it works as seamlessly as possible.