ownership¶
Ownership is important when managing resources in C++. sol has many ownership semantics which are generally safe by default. Below are the rules.
object ownership¶
You can take a reference to something that exists in Lua by pulling out a sol::reference or a sol::object:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <string>
#include <iostream>
int main () {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script(R"(
obj = "please don't let me die";
)");
sol::object keep_alive = lua["obj"];
lua.script(R"(
obj = nil;
function say(msg)
print(msg)
end
)");
lua.collect_garbage();
lua["say"](lua["obj"]);
// still accessible here and still alive in Lua
// even though the name was cleared
std::string message = keep_alive.as<std::string>();
std::cout << message << std::endl;
// Can be pushed back into Lua as an argument
// or set to a new name,
// whatever you like!
lua["say"](keep_alive);
return 0;
}
|
All objects must be destroyed before the sol::state is destroyed, otherwise you will end up with dangling references to the Lua State and things will explode in horrible, terrible fashion.
This applies to more than just sol::object: all types derived from sol::reference and sol::object (sol::table sol::userdata, etc.) must be cleaned up before the state goes out of scope.
pointer ownership¶
sol will not take ownership of raw pointers: raw pointers do not own anything. sol will not delete raw pointers, because they do not (and are not supposed to) own anything:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
struct my_type {
void stuff() {
}
};
int main() {
sol::state lua;
// AAAHHH BAD
// dangling pointer!
lua["my_func"] = []() -> my_type* { return new my_type(); };
// AAAHHH!
lua.set("something", new my_type());
// AAAAAAHHH!!!
lua["something_else"] = new my_type();
return 0;
}
|
Use/return a unique_ptr
or shared_ptr
instead or just return a value:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
struct my_type {
void stuff() {
}
};
int main() {
sol::state lua;
// :ok:
lua["my_func0"] = []() -> std::unique_ptr<my_type> { return std::make_unique<my_type>(); };
// :ok:
lua["my_func1"] = []() -> std::shared_ptr<my_type> { return std::make_shared<my_type>(); };
// :ok:
lua["my_func2"] = []() -> my_type { return my_type(); };
// :ok:
lua.set("something", std::unique_ptr<my_type>(new my_type()));
std::shared_ptr<my_type> my_shared = std::make_shared<my_type>();
// :ok:
lua.set("something_else", my_shared);
// :ok:
auto my_unique = std::make_unique<my_type>();
lua["other_thing"] = std::move(my_unique);
return 0;
}
|
If you have something you know is going to last and you just want to give it to Lua as a reference, then it’s fine too:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
struct my_type {
void stuff() {
}
};
int main() {
sol::state lua;
lua["my_func5"] = []() -> my_type* {
static my_type mt;
return &mt;
};
return 0;
}
|
sol can detect nullptr
, so if you happen to return it there won’t be any dangling because a sol::lua_nil
will be pushed. But if you know it’s nil
beforehand, please return std::nullptr_t
or sol::lua_nil
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
struct my_type {
void stuff() {
}
};
int main() {
sol::state lua;
// THIS IS STILL BAD DON'T DO IT AAAHHH BAD
// return a unique_ptr that's empty instead
// or be explicit!
lua["my_func6"] = []() -> my_type* { return nullptr; };
// :ok:
lua["my_func7"] = []() -> std::nullptr_t { return nullptr; };
// :ok:
lua["my_func8"] = []() -> std::unique_ptr<my_type> {
// default-constructs as a nullptr,
// gets pushed as nil to Lua
return std::unique_ptr<my_type>();
// same happens for std::shared_ptr
};
// Acceptable, it will set 'something' to nil
// (and delete it on next GC if there's no more references)
lua.set("something", nullptr);
// Also fine
lua["something_else"] = nullptr;
return 0;
}
|
ephermeal (proxy) objects¶
Proxy and result types are ephermeal. They rely on the Lua stack and their constructors / destructors interact with the Lua stack. This means they are entirely unsafe to return from functions in C++, without very careful attention paid to how they are used that often requires relying on implementation-defined behaviors.
Please be careful when using (protected_)function_result, load_result (especially multiple load/function results in a single C++ function!) stack_reference, and similar stack-based things. If you want to return these things, consider