-
-
Notifications
You must be signed in to change notification settings - Fork 529
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
214 additions
and
268 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,127 +1,58 @@ | ||
ownership | ||
========= | ||
|
||
You can take a reference to something that exists in Lua by pulling out a :doc:`sol::reference<../api/reference>` or a :doc:`sol::object<../api/object>`: | ||
Ownership is important when managing resources in C++. sol has many ownership semantics which are generally safe by default. Below are the rules. | ||
|
||
.. code-block:: cpp | ||
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); | ||
object ownership | ||
---------------- | ||
|
||
You can take a reference to something that exists in Lua by pulling out a :doc:`sol::reference<../api/reference>` or a :doc:`sol::object<../api/object>`: | ||
|
||
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: | ||
.. literalinclude:: ../../../examples/source/tutorials/object_lifetime.cpp | ||
:linenos: | ||
:caption: object_lifetime.cpp | ||
|
||
.. code-block:: cpp | ||
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. | ||
|
||
struct my_type { | ||
void stuff () {} | ||
}; | ||
This applies to more than just `sol::object`: all types derived from `sol::reference` and `sol::object` (:doc:`sol::table<../api/table>` :doc:`sol::userdata<../api/userdata>`, etc.) must be cleaned up before the state goes out of scope. | ||
|
||
sol::state lua; | ||
|
||
// AAAHHH BAD | ||
// dangling pointer! | ||
lua["my_func"] = []() -> my_type* { | ||
return new my_type(); | ||
}; | ||
pointer ownership | ||
----------------- | ||
|
||
// AAAHHH! | ||
lua.set("something", new my_type()); | ||
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: | ||
|
||
// AAAAAAHHH!!! | ||
lua["something_else"] = new my_type(); | ||
.. literalinclude:: ../../../examples/source/tutorials/pointer_lifetime.cpp | ||
:linenos: | ||
:caption: pointer_lifetime.cpp | ||
:lines: 1-11,14-22, 74- | ||
|
||
Use/return a ``unique_ptr`` or ``shared_ptr`` instead or just return a value: | ||
|
||
.. code-block:: cpp | ||
// :ok: | ||
lua["my_func"] = []() -> std::unique_ptr<my_type> { | ||
return std::make_unique<my_type>(); | ||
}; | ||
// :ok: | ||
lua["my_func"] = []() -> std::shared_ptr<my_type> { | ||
return std::make_shared<my_type>(); | ||
}; | ||
// :ok: | ||
lua["my_func"] = []() -> 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); | ||
auto my_unique = std::make_unique<my_type>(); | ||
lua["other_thing"] = std::move(my_unique); | ||
.. literalinclude:: ../../../examples/source/tutorials/pointer_lifetime.cpp | ||
:linenos: | ||
:caption: (smart pointers) pointer_lifetime.cpp | ||
:lines: 1-11,14-22, 74- | ||
|
||
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: | ||
|
||
.. code-block:: cpp | ||
// :ok: | ||
lua["my_func"] = []() -> my_type* { | ||
static my_type mt; | ||
return &mt; | ||
}; | ||
sol can detect ``nullptr``, so if you happen to return it there won't be any dangling because a ``sol::nil`` will be pushed. | ||
.. literalinclude:: ../../../examples/source/tutorials/pointer_lifetime.cpp | ||
:linenos: | ||
:caption: (static) pointer_lifetime.cpp | ||
:lines: 1-11,46-49,74- | ||
|
||
.. code-block:: cpp | ||
|
||
struct my_type { | ||
void stuff () {} | ||
}; | ||
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``: | ||
|
||
sol::state lua; | ||
.. literalinclude:: ../../../examples/source/tutorials/pointer_lifetime.cpp | ||
:linenos: | ||
:caption: (nil/nullptr) pointer_lifetime.cpp | ||
:lines: 1-11,51- | ||
|
||
// BUT THIS IS STILL BAD DON'T DO IT AAAHHH BAD | ||
// return a unique_ptr still or something! | ||
lua["my_func"] = []() -> my_type* { | ||
return nullptr; | ||
}; | ||
|
||
lua["my_func_2"] = [] () -> 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 | ||
} | ||
ephermeal (proxy) objects | ||
------------------------- | ||
|
||
// Acceptable, it will set 'something' to nil | ||
// (and delete it on next GC if there's no more references) | ||
lua.set("something", nullptr); | ||
:doc:`Proxy<../api/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. | ||
|
||
// Also fine | ||
lua["something_else"] = nullptr; | ||
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 |
Oops, something went wrong.