diff --git a/README.md b/README.md index 25a3f826..5c912331 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,13 @@ If you use CMake, you can also configure and generate a project that will genera ## Features - [Fastest in the land](http://sol2.readthedocs.io/en/latest/benchmarks.html) (see: sol bar in graph). -- Supports retrieval and setting of multiple types including `std::string` and `std::map/unordered_map`. +- Supports retrieval and setting of multiple types including: + * `std::string`, `std::wstring`, `std::u16string` and `std::u32string` support (and for views). + * understands and works with containers such as `std::map/unordered_map`, c-style arrays, vectors, non-standard custom containers and more. + * user-defined types, with or **without** registering that type + * `std::unique_ptr`, `std::shared_ptr`, and optional support of other pointer types like `boost::shared_ptr`. + * custom `optional` that works with references. + * C++17 support for variants and similar new types. - Lambda, function, and member function bindings are supported. - Intermediate type for checking if a variable exists. - Simple API that completely abstracts away the C stack API, including `protected_function` with the ability to use an error-handling function. @@ -82,6 +88,7 @@ If you use CMake, you can also configure and generate a project that will genera - Customization points to allow your C++ objects to be pushed and retrieved from Lua as multiple consecutive objects, or anything else you desire! - Overloaded function calls: `my_function(1); my_function("Hello")` in the same lua script route to different function calls based on parameters - Support for tables, nested tables, table iteration with `table.for_each` / `begin()` and `end()` iterators. +- Zero overhead for usertype function call lookup when using `SOL_USE_BOOST`, safe for critical applications ## Supported Compilers diff --git a/docs/source/api/unique_usertype_traits.rst b/docs/source/api/unique_usertype_traits.rst index 62e0e407..723f0991 100644 --- a/docs/source/api/unique_usertype_traits.rst +++ b/docs/source/api/unique_usertype_traits.rst @@ -18,7 +18,7 @@ unique_usertype_traits static type* get (const actual_type&) {...} }; -This is a customization point for users who need to *work with special kinds of pointers/handles*. The traits type alerts the library that a certain type is to be pushed as a special userdata with special deletion / destruction semantics, like many smart pointers / custom smart pointers / handles It is already defined for ``std::unique_ptr`` and ``std::shared_ptr``. You can specialize this to get ``unique_usertype_traits`` semantics with your code. For example, here is how ``boost::shared_ptr`` would look: +This is a customization point for users who need to *work with special kinds of pointers/handles*. The traits type alerts the library that a certain type is to be pushed as a special userdata with special deletion / destruction semantics, like many smart pointers / custom smart pointers / handles. It is already defined for ``std::unique_ptr`` and ``std::shared_ptr`` and works properly with those types (see `shared_ptr here`_ and `unique_ptr here`_ for examples). You can specialize this to get ``unique_usertype_traits`` semantics with your code. For example, here is how ``boost::shared_ptr`` would look: .. code-block:: cpp @@ -44,3 +44,7 @@ This will allow the library to properly handle ``boost::shared_ptr``, with re .. note:: If ``is_null`` triggers (returns ``true``), a ``nil`` value will be pushed into Lua rather than an empty structure. + + +.. _shared_ptr here: https://github.com/ThePhD/sol2/blob/develop/examples/shared_ptr.cpp +.. _unique_ptr here: https://github.com/ThePhD/sol2/blob/develop/examples/unique_ptr.cpp diff --git a/examples/shared_ptr.cpp b/examples/shared_ptr.cpp new file mode 100644 index 00000000..149b1fba --- /dev/null +++ b/examples/shared_ptr.cpp @@ -0,0 +1,95 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include + +struct my_type { + int value = 10; + + my_type() { + std::cout << "my_type at " << static_cast(this) << " being default constructed!" << std::endl; + } + + my_type(const my_type& other) : value(other.value) { + std::cout << "my_type at " << static_cast(this) << " being copy constructed!" << std::endl; + } + + my_type(my_type&& other) : value(other.value) { + std::cout << "my_type at " << static_cast(this) << " being move-constructed!" << std::endl; + } + + my_type& operator=(const my_type& other) { + value = other.value; + std::cout << "my_type at " << static_cast(this) << " being copy-assigned to!" << std::endl; + } + + my_type& operator=(my_type&& other) { + value = other.value; + std::cout << "my_type at " << static_cast(this) << " being move-assigned to!" << std::endl; + } + + ~my_type() { + std::cout << "my_type at " << static_cast(this) << " being destructed!" << std::endl; + } +}; + +int main() { + + std::cout << "=== shared_ptr support ===" << std::endl; + + sol::state lua; + lua.new_usertype("my_type", + "value", &my_type::value + ); + { + std::shared_ptr shared = std::make_shared(); + lua["shared"] = std::move(shared); + } + { + std::cout << "getting reference to shared_ptr..." << std::endl; + std::shared_ptr& ref_to_shared_ptr = lua["shared"]; + std::cout << "\tshared.use_count(): " << ref_to_shared_ptr.use_count() << std::endl; + my_type& ref_to_my_type = lua["shared"]; + std::cout << "\tafter getting reference to my_type: " << ref_to_shared_ptr.use_count() << std::endl; + my_type* ptr_to_my_type = lua["shared"]; + std::cout << "\tafter getting pointer to my_type: " << ref_to_shared_ptr.use_count() << std::endl; + + c_assert(ptr_to_my_type == ref_to_shared_ptr.get()); + c_assert(&ref_to_my_type == ref_to_shared_ptr.get()); + c_assert(ref_to_shared_ptr->value == 10); + + // script affects all of them equally + lua.script("shared.value = 20"); + + c_assert(ptr_to_my_type->value == 20); + c_assert(ref_to_my_type.value == 20); + c_assert(ref_to_shared_ptr->value == 20); + } + { + std::cout << "getting copy of shared_ptr..." << std::endl; + std::shared_ptr copy_of_shared_ptr = lua["shared"]; + std::cout << "\tshared.use_count(): " << copy_of_shared_ptr.use_count() << std::endl; + my_type copy_of_value = lua["shared"]; + std::cout << "\tafter getting value copy of my_type: " << copy_of_shared_ptr.use_count() << std::endl; + + c_assert(copy_of_shared_ptr->value == 20); + c_assert(copy_of_value.value == 20); + + // script still affects pointer, but does not affect copy of `my_type` + lua.script("shared.value = 30"); + + c_assert(copy_of_shared_ptr->value == 30); + c_assert(copy_of_value.value == 20); + } + // set to nil and collect garbage to destroy it + lua.script("shared = nil"); + lua.collect_garbage(); + lua.collect_garbage(); + + std::cout << "garbage has been collected" << std::endl; + std::cout << std::endl; + + return 0; +} diff --git a/examples/unique_ptr.cpp b/examples/unique_ptr.cpp new file mode 100644 index 00000000..399de564 --- /dev/null +++ b/examples/unique_ptr.cpp @@ -0,0 +1,87 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include + +struct my_type { + int value = 10; + + my_type() { + std::cout << "my_type at " << static_cast(this) << " being default constructed!" << std::endl; + } + + my_type(const my_type& other) : value(other.value) { + std::cout << "my_type at " << static_cast(this) << " being copy constructed!" << std::endl; + } + + my_type(my_type&& other) : value(other.value) { + std::cout << "my_type at " << static_cast(this) << " being move-constructed!" << std::endl; + } + + my_type& operator=(const my_type& other) { + value = other.value; + std::cout << "my_type at " << static_cast(this) << " being copy-assigned to!" << std::endl; + } + + my_type& operator=(my_type&& other) { + value = other.value; + std::cout << "my_type at " << static_cast(this) << " being move-assigned to!" << std::endl; + } + + ~my_type() { + std::cout << "my_type at " << static_cast(this) << " being destructed!" << std::endl; + } +}; + +int main() { + + std::cout << "=== unique_ptr support ===" << std::endl; + + sol::state lua; + lua.new_usertype("my_type", + "value", &my_type::value + ); + { + std::unique_ptr unique = std::make_unique(); + lua["unique"] = std::move(unique); + } + { + std::cout << "getting reference to unique_ptr..." << std::endl; + std::unique_ptr& ref_to_unique_ptr = lua["unique"]; + my_type& ref_to_my_type = lua["unique"]; + my_type* ptr_to_my_type = lua["unique"]; + + c_assert(ptr_to_my_type == ref_to_unique_ptr.get()); + c_assert(&ref_to_my_type == ref_to_unique_ptr.get()); + c_assert(ref_to_unique_ptr->value == 10); + + // script affects all of them equally + lua.script("unique.value = 20"); + + c_assert(ptr_to_my_type->value == 20); + c_assert(ref_to_my_type.value == 20); + c_assert(ref_to_unique_ptr->value == 20); + } + { + std::cout << "getting copy of unique_ptr..." << std::endl; + my_type copy_of_value = lua["unique"]; + + c_assert(copy_of_value.value == 20); + + // script still affects pointer, but does not affect copy of `my_type` + lua.script("unique.value = 30"); + + c_assert(copy_of_value.value == 20); + } + // set to nil and collect garbage to destroy it + lua.script("unique = nil"); + lua.collect_garbage(); + lua.collect_garbage(); + + std::cout << "garbage has been collected" << std::endl; + std::cout << std::endl; + + return 0; +}