Proper std::ref semantics throughout the codebase's get/set.

Also a convenience type `sol::ref`, which is just an
alias to `std::reference_wrapper`, to enable easy getting with `lua.get<>`
(we can't use `lua.get<some_type>` because of necessary `Unqualified<T>` use)
This commit is contained in:
ThePhD 2014-09-18 00:23:46 -04:00
parent ad83552072
commit 6121da334f
3 changed files with 62 additions and 6 deletions

View File

@ -127,6 +127,22 @@ struct getter<T*> {
} }
}; };
template<typename T>
struct getter<T&> {
static T& get(lua_State* L, int index = -1) {
void* udata = lua_touserdata(L, index);
T** obj = static_cast<T**>(udata);
return **obj;
}
};
template<typename T>
struct getter<std::reference_wrapper<T>> {
static T& get(lua_State* L, int index = -1) {
return getter<T&>{}.get(L, index);
}
};
template<> template<>
struct getter<type> { struct getter<type> {
static type get(lua_State *L, int index){ static type get(lua_State *L, int index){
@ -243,7 +259,7 @@ struct pusher {
template<typename U = Unqualified<T>, EnableIf<Not<has_begin_end<U>>, Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0> template<typename U = Unqualified<T>, EnableIf<Not<has_begin_end<U>>, Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0>
static void push(lua_State* L, T& t) { static void push(lua_State* L, T& t) {
pusher<T*>{}.push(L, std::addressof(t)); detail::push_userdata<U>(L, userdata_traits<T>::metatable, t);
} }
template<typename U = Unqualified<T>, EnableIf<Not<has_begin_end<U>>, Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0> template<typename U = Unqualified<T>, EnableIf<Not<has_begin_end<U>>, Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0>
@ -259,6 +275,13 @@ struct pusher<T*> {
} }
}; };
template<typename T>
struct pusher<std::reference_wrapper<T>> {
static void push(lua_State* L, const std::reference_wrapper<T>& t) {
pusher<T*>{}.push(L, std::addressof(t.get()));
}
};
template<> template<>
struct pusher<bool> { struct pusher<bool> {
static void push(lua_State* L, const bool& b) { static void push(lua_State* L, const bool& b) {

View File

@ -93,6 +93,9 @@ struct constructors {};
const auto default_constructor = constructors<types<>>{}; const auto default_constructor = constructors<types<>>{};
template <typename T>
using ref = std::reference_wrapper<T>;
} // sol } // sol
#endif // SOL_TUPLE_HPP #endif // SOL_TUPLE_HPP

View File

@ -84,6 +84,18 @@ struct self_test {
} }
}; };
struct vars {
vars () {
}
int boop = 0;
~vars () {
}
};
struct object { struct object {
std::string operator() () { std::string operator() () {
std::cout << "member_test()" << std::endl; std::cout << "member_test()" << std::endl;
@ -469,7 +481,7 @@ TEST_CASE("functions/return_order_and_multi_get", "Check if return order is in t
auto tluaget = lua.get<int, int, int>("x", "y", "z"); auto tluaget = lua.get<int, int, int>("x", "y", "z");
std::cout << "cpp: " << std::get<0>(tcpp) << ',' << std::get<1>(tcpp) << ',' << std::get<2>(tcpp) << std::endl; std::cout << "cpp: " << std::get<0>(tcpp) << ',' << std::get<1>(tcpp) << ',' << std::get<2>(tcpp) << std::endl;
std::cout << "lua: " << std::get<0>(tlua) << ',' << std::get<1>(tlua) << ',' << std::get<2>(tlua) << std::endl; std::cout << "lua: " << std::get<0>(tlua) << ',' << std::get<1>(tlua) << ',' << std::get<2>(tlua) << std::endl;
std::cout << "lua.xyz: " << lua.get<int>("x") << ',' << lua.get<int>("y") << ',' << lua.get<int>("z") << std::endl; std::cout << "lua xyz: " << lua.get<int>("x") << ',' << lua.get<int>("y") << ',' << lua.get<int>("z") << std::endl;
REQUIRE(tcpp == triple); REQUIRE(tcpp == triple);
REQUIRE(tlua == triple); REQUIRE(tlua == triple);
REQUIRE(tluaget == triple); REQUIRE(tluaget == triple);
@ -857,10 +869,6 @@ TEST_CASE("userdata/nonmember functions implement functionality", "let users set
} }
TEST_CASE("regressions/one", "issue number 48") { TEST_CASE("regressions/one", "issue number 48") {
struct vars {
int boop = 0;
};
sol::state lua; sol::state lua;
lua.new_userdata<vars>("vars", "boop", &vars::boop); lua.new_userdata<vars>("vars", "boop", &vars::boop);
REQUIRE_NOTHROW(lua.script("beep = vars.new()\n" REQUIRE_NOTHROW(lua.script("beep = vars.new()\n"
@ -871,3 +879,25 @@ TEST_CASE("regressions/one", "issue number 48") {
auto* ptr = &my_var; auto* ptr = &my_var;
REQUIRE(ptr->boop == 1); REQUIRE(ptr->boop == 1);
} }
TEST_CASE("references/get-set", "properly get and set with std::ref semantics. Note that to get, we must not use Unqualified<T> on the type...") {
sol::state lua;
lua.new_userdata<vars>("vars",
"boop", &vars::boop);
vars var{};
vars rvar{};
lua.set("beep", var);
lua.set("rbeep", std::ref(rvar));
auto& my_var = lua.get<vars>("beep");
auto& ref_var = lua.get<sol::ref<vars>>("rbeep");
var.boop = 2;
rvar.boop = 5;
REQUIRE((my_var.boop == 0));
REQUIRE(var.boop != my_var.boop);
// Reference should point back to the same type.
REQUIRE(rvar.boop == ref_var.boop);
}