vet container_usertype and wrap for the proper traits regarding assignment and checking of said assignments

update documentation with more tips and other similar things
This commit is contained in:
ThePhD 2017-05-20 20:01:04 -04:00
parent 5ff394ae0d
commit 6047f48343
5 changed files with 63 additions and 8 deletions

View File

@ -8,7 +8,9 @@ routine to mark a member variable as read-only
template <typename T> template <typename T>
auto readonly( T&& value ); auto readonly( T&& value );
The goal of read-only is to protect a variable set on a usertype or a function. Simply wrap it around a member variable, e.g. ``sol::readonly( &my_class::my_member_variable )`` in the appropriate place to use it. If someone tries to set it, it will throw an error. This can ONLY work on :doc:`usertypes<usertype>` and when you specifically set a member variable as a function and wrap it with this. It will NOT work anywhere else: doing so will invoke compiler errors. The goal of read-only is to protect a variable set on a usertype or a function. Simply wrap it around a member variable, e.g. ``sol::readonly( &my_class::my_member_variable )`` in the appropriate place to use it. If someone tries to set it, it will throw an error.
``sol::readonly`` is especially important when you're working with types that do not have a copy constructor. Lua does not understand move semantics, and therefore setters to user-defined-types require a C++ copy constructor. Containers as member variables that contain types that are not copyable but movable -- e.g. ``std::vector<my_move_only_type>`` amongst others -- also can erroneously state they are copyable but fail with compiler errors. If your type does not fit a container's definition of being copyable or is just not copyable in general and it is a member variable, please use ``sol::readonly``.
If you are looking to make a read-only table, you need to go through a bit of a complicated song and dance by overriding the ``__index`` metamethod. Here's a complete example on the way to do that using ``sol``: If you are looking to make a read-only table, you need to go through a bit of a complicated song and dance by overriding the ``__index`` metamethod. Here's a complete example on the way to do that using ``sol``:

View File

@ -61,7 +61,7 @@ author = 'ThePhD'
# The short X.Y version. # The short X.Y version.
version = '2.17' version = '2.17'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '2.17.3' release = '2.17.4'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -10,6 +10,16 @@ Running Scripts
Scripts can have syntax errors, can load from the file system wrong, or have runtime issues. Knowing which one can be troublesome. There are various small building blocks to load and run code, but to check errors you can use the overloaded :ref:`script/script_file functions on sol::state/sol::state_view<state-script-function>` Scripts can have syntax errors, can load from the file system wrong, or have runtime issues. Knowing which one can be troublesome. There are various small building blocks to load and run code, but to check errors you can use the overloaded :ref:`script/script_file functions on sol::state/sol::state_view<state-script-function>`
Compiler Errors / Warnings
--------------------------
A myriad of compiler errors can occur when something goes wrong. Here is some basic advice about working with these types:
* If there are a myriad of errors relating to ``std::index_sequence``, type traits, and other ``std::`` members, it is likely you have not turned on your C++14 switch for your compiler. Visual Studio 2015 turns these on by default, but g++ and clang++ do not have them as defaults and you should pass the flag ``--std=c++1y`` or ``--std=c++14``, or similar for your compiler.
* Sometimes, a generated usertype can be very long if you are binding a lot of member functions. You may end up with a myriad of warnings about debug symbols being cut off or about ``__LINE_VAR`` exceeding maximum length. You can silence these warnings safely for some compilers.
* Template depth errors may also be a problem on earlier versions of clang++ and g++. Use ``-ftemplate-depth`` compiler flag and specify really high number (something like 2048 or even double that amount) to let the compiler work freely. Also consider potentially using :doc:`simple usertypes<api/simple_usertype>` to save compilation speed.
* If you have a move-only type, that type may need to be made ``readonly`` if it is bound as a member variable on a usertype or bound using ``state_view::set_function``. See :doc:`sol::readonly<api/readonly>` for more details.
Linker Errors Linker Errors
------------- -------------

View File

@ -247,7 +247,7 @@ namespace sol {
auto it = begin(src); auto it = begin(src);
--k; --k;
if (k == src.size()) { if (k == src.size()) {
real_add_call_push(std::integral_constant<bool, detail::has_push_back<T>::value>(), L, src, 1); real_add_call_push(std::integral_constant<bool, detail::has_push_back<T>::value && std::is_copy_constructible<V>::value>(), L, src, 1);
return 0; return 0;
} }
std::advance(it, k); std::advance(it, k);
@ -256,7 +256,7 @@ namespace sol {
} }
static int real_new_index_call(lua_State* L) { static int real_new_index_call(lua_State* L) {
return real_new_index_call_const(meta::neg<meta::any<std::is_const<V>, std::is_const<IR>>>(), is_associative(), L); return real_new_index_call_const(meta::neg<meta::any<std::is_const<V>, std::is_const<IR>, meta::neg<std::is_copy_assignable<V>>>>(), is_associative(), L);
} }
static int real_pairs_next_call_assoc(std::true_type, lua_State* L) { static int real_pairs_next_call_assoc(std::true_type, lua_State* L) {
@ -334,7 +334,7 @@ namespace sol {
} }
static int real_add_call_push(std::false_type, lua_State*L, T& src, int boost = 0) { static int real_add_call_push(std::false_type, lua_State*L, T& src, int boost = 0) {
return real_add_call_insert(std::integral_constant<bool, detail::has_insert<T>::value>(), L, src, boost); return real_add_call_insert(std::integral_constant<bool, detail::has_insert<T>::value && std::is_copy_constructible<V>::value>(), L, src, boost);
} }
static int real_add_call_associative(std::true_type, lua_State* L) { static int real_add_call_associative(std::true_type, lua_State* L) {
@ -343,7 +343,7 @@ namespace sol {
static int real_add_call_associative(std::false_type, lua_State* L) { static int real_add_call_associative(std::false_type, lua_State* L) {
auto& src = get_src(L); auto& src = get_src(L);
return real_add_call_push(std::integral_constant<bool, detail::has_push_back<T>::value>(), L, src); return real_add_call_push(std::integral_constant<bool, detail::has_push_back<T>::value && std::is_copy_constructible<V>::value>(), L, src);
} }
static int real_add_call_capable(std::true_type, lua_State* L) { static int real_add_call_capable(std::true_type, lua_State* L) {
@ -356,7 +356,7 @@ namespace sol {
} }
static int real_add_call(lua_State* L) { static int real_add_call(lua_State* L) {
return real_add_call_capable(std::integral_constant<bool, detail::has_push_back<T>::value || detail::has_insert<T>::value>(), L); return real_add_call_capable(std::integral_constant<bool, (detail::has_push_back<T>::value || detail::has_insert<T>::value) && std::is_copy_constructible<V>::value>(), L);
} }
static int real_insert_call_capable(std::false_type, std::false_type, lua_State*L) { static int real_insert_call_capable(std::false_type, std::false_type, lua_State*L) {
@ -380,7 +380,7 @@ namespace sol {
} }
static int real_insert_call(lua_State*L) { static int real_insert_call(lua_State*L) {
return real_insert_call_capable(std::integral_constant<bool, detail::has_insert<T>::value>(), is_associative(), L); return real_insert_call_capable(std::integral_constant<bool, detail::has_insert<T>::value && std::is_copy_assignable<V>::value>(), is_associative(), L);
} }
static int real_clear_call_capable(std::false_type, lua_State* L) { static int real_clear_call_capable(std::false_type, lua_State* L) {

View File

@ -578,3 +578,46 @@ end
REQUIRE(vec2.size() == 4); REQUIRE(vec2.size() == 4);
REQUIRE(vec2 == append_cmp); REQUIRE(vec2 == append_cmp);
} }
TEST_CASE("containers/non_copyable", "make sure non-copyable types in containers behave properly when stored as a member variable in a bound usertype") {
struct non_copyable {
non_copyable(non_copyable&& other) noexcept = default;
non_copyable& operator=(non_copyable&& other) noexcept = default;
non_copyable(const non_copyable& other) noexcept = delete;
non_copyable& operator=(const non_copyable& other) noexcept = delete;
};
struct test {
std::vector<non_copyable> b;
test() : b() {}
test(test&&) = default;
test& operator=(test&&) = default;
test(const test&) = delete;
test& operator=(const test&) = delete;
};
SECTION("normal") {
sol::state lua;
lua.new_usertype<test>("test",
"b", sol::readonly(&test::b)
);
lua["v"] = std::vector<non_copyable>{};
REQUIRE_THROWS([&lua]() {
lua.script("t = test.new()\nt.b = v");
}());
}
SECTION("simple") {
sol::state lua;
lua.new_simple_usertype<test>("test",
"b", sol::readonly(&test::b)
);
lua["v"] = std::vector<non_copyable>{};
REQUIRE_THROWS([&lua]() {
lua.script("t = test.new()\nt.b = v");
}());
}
}