operator[] support~

And all is as it should be in the world~
This commit is contained in:
ThePhD 2013-12-21 19:30:30 -05:00
parent a6120631d8
commit b90f789986
5 changed files with 271 additions and 9 deletions

129
sol/proxy.hpp Normal file
View File

@ -0,0 +1,129 @@
// The MIT License (MIT)
// Copyright (c) 2013 Danny Y., Rapptz
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "traits.hpp"
#include "object.hpp"
#include "function.hpp"
namespace sol {
template <typename Table, typename Key>
struct proxy {
private:
Table& tbl;
Key& key;
public:
template <typename T>
proxy( Table& table, T&& key ) : tbl( table ), key( std::forward<T>( key ) ) {
}
template<typename T>
T get( ) const {
return tbl.get<T>( key );
}
template<typename T>
proxy& set( T&& item ) {
tbl.set( key, std::forward<T>( item ) );
return *this;
}
template<typename... Args>
proxy& set_function( Args&&... args ) {
tbl.set_function( key, std::forward<Args>( args ).. );
return *this;
}
template<typename U>
EnableIf<Function<Unqualified<U>>> operator=( U&& other ) {
tbl.set_function( key, std::forward<U>( other ) );
}
template<typename U>
DisableIf<Function<Unqualified<U>>> operator=( U&& other ) {
tbl.set( key, std::forward<U>( other ) );
}
operator nil_t ( ) {
return get<nil_t>( );
}
operator bool( ) {
return get<bool>( );
}
operator std::string( ) {
return get<std::string>( );
}
operator object( ) {
return get<object>( );
}
operator function( ) {
return get<function>( );
}
operator double( ) {
return get<double>( );
}
operator float( ) {
return get<float>( );
}
operator int( ) {
return get<int>( );
}
template<typename... Ret, typename... Args>
typename multi_return<Ret...>::type call( Args&&... args ) {
return tbl.get<function>(key)(types<Ret...>(), std::forward<Args>( args )...);
}
};
template <typename Table, typename Key, typename T>
bool operator== ( T&& left, const proxy<Table, Key>& right ) {
typedef Decay<T> T_;
return right.get<T_>( ) == left;
}
template <typename Table, typename Key, typename T>
bool operator== ( const proxy<Table, Key>& right, T&& left ) {
typedef Decay<T> T_;
return right.get<T_>( ) == left;
}
template <typename Table, typename Key, typename T>
bool operator!= ( T&& left, const proxy<Table, Key>& right ) {
typedef Decay<T> T_;
return right.get<T_>( ) != left;
}
template <typename Table, typename Key, typename T>
bool operator!= ( const proxy<Table, Key>& right, T&& left ) {
typedef Decay<T> T_;
return right.get<T_>( ) != left;
}
} // sol

View File

@ -173,6 +173,16 @@ public:
table registry() const { table registry() const {
return reg; return reg;
} }
template <typename T>
proxy<table, T> operator[]( T&& key ) {
return global[ std::forward<T>(key) ];
}
template <typename T>
proxy<const table, T> operator[]( T&& key ) const {
return global[ std::forward<T>( key ) ];
}
}; };
} // sol } // sol

View File

@ -22,6 +22,7 @@
#ifndef SOL_TABLE_HPP #ifndef SOL_TABLE_HPP
#define SOL_TABLE_HPP #define SOL_TABLE_HPP
#include "proxy.hpp"
#include "stack.hpp" #include "stack.hpp"
#include "lua_function.hpp" #include "lua_function.hpp"
#include <array> #include <array>
@ -117,6 +118,17 @@ public:
push(); push();
return lua_rawlen(state(), -1); return lua_rawlen(state(), -1);
} }
template <typename T>
proxy<table, T> operator[]( T&& key ) {
return proxy<table, T>( *this, std::forward<T>( key ) );
}
template <typename T>
proxy<const table, T> operator[]( T&& key ) const {
return proxy<const table, T>( *this, std::forward<T>( key ) );
}
private: private:
template<typename T, typename TFx> template<typename T, typename TFx>
table& set_isfunction_fx(std::true_type, T&& key, TFx&& fx) { table& set_isfunction_fx(std::true_type, T&& key, TFx&& fx) {

View File

@ -26,6 +26,29 @@
#include <type_traits> #include <type_traits>
namespace sol { namespace sol {
namespace detail {
template<typename T, bool isclass = std::is_class<Unqualified<T>>::value>
struct is_function_impl : std::is_function<typename std::remove_pointer<T>::type> { };
template<typename T>
struct is_function_impl<T, true> {
using yes = char;
using no = struct { char s[ 2 ]; };
struct F { void operator()( ); };
struct Derived : T, F { };
template<typename U, U> struct Check;
template<typename V>
static no test( Check<void ( F::* )( ), &V::operator()>* );
template<typename>
static yes test( ... );
static const bool value = sizeof( test<Derived>( 0 ) ) == sizeof( yes );
};
} // detail
template<typename T, typename R = void> template<typename T, typename R = void>
using EnableIf = typename std::enable_if<T::value, R>::type; using EnableIf = typename std::enable_if<T::value, R>::type;
@ -55,6 +78,8 @@ struct multi_return<> : types<>{
template<bool B> template<bool B>
using Bool = std::integral_constant<bool, B>; using Bool = std::integral_constant<bool, B>;
template<typename T>
struct Function : Bool<detail::is_function_impl<T>::value> { };
template<typename TFuncSignature> template<typename TFuncSignature>
struct function_traits; struct function_traits;

104
tests.cpp
View File

@ -56,7 +56,7 @@ TEST_CASE("simple/get", "Tests if the get function works properly.") {
REQUIRE(e == true); REQUIRE(e == true);
} }
TEST_CASE("simple/addition", "") { TEST_CASE("simple/addition", "check if addition works and can be gotten through lua.get and lua.set") {
sol::state lua; sol::state lua;
lua.set("b", 0.2); lua.set("b", 0.2);
@ -66,7 +66,7 @@ TEST_CASE("simple/addition", "") {
REQUIRE(c == 9.2); REQUIRE(c == 9.2);
} }
TEST_CASE("simple/if", "") { TEST_CASE("simple/if", "check if if statements work through lua") {
sol::state lua; sol::state lua;
std::string program = "if true then f = 0.1 else f = 'test' end"; std::string program = "if true then f = 0.1 else f = 'test' end";
@ -74,9 +74,10 @@ TEST_CASE("simple/if", "") {
auto f = lua.get<double>("f"); auto f = lua.get<double>("f");
REQUIRE(f == 0.1); REQUIRE(f == 0.1);
REQUIRE((f == lua["f"]));
} }
TEST_CASE("simple/callWithParameters", "Lua function is called with a few parameters from C++") { TEST_CASE("simple/call_with_parameters", "Lua function is called with a few parameters from C++") {
sol::state lua; sol::state lua;
REQUIRE_NOTHROW(lua.script("function my_add(i, j, k) return i + j + k end")); REQUIRE_NOTHROW(lua.script("function my_add(i, j, k) return i + j + k end"));
@ -90,7 +91,7 @@ TEST_CASE("simple/callWithParameters", "Lua function is called with a few parame
REQUIRE_THROWS(a = f.call<int>(1, 2, "arf")); REQUIRE_THROWS(a = f.call<int>(1, 2, "arf"));
} }
TEST_CASE("simple/callCppFunction", "C++ function is called from lua") { TEST_CASE("simple/call_c++_function", "C++ function is called from lua") {
sol::state lua; sol::state lua;
lua.set_function("plop_xyz", plop_xyz); lua.set_function("plop_xyz", plop_xyz);
@ -99,7 +100,7 @@ TEST_CASE("simple/callCppFunction", "C++ function is called from lua") {
REQUIRE(lua.get<int>("x") == 11); REQUIRE(lua.get<int>("x") == 11);
} }
TEST_CASE("simple/callLambda", "A C++ lambda is exposed to lua and called") { TEST_CASE("simple/call_lambda", "A C++ lambda is exposed to lua and called") {
sol::state lua; sol::state lua;
int x = 0; int x = 0;
@ -111,7 +112,7 @@ TEST_CASE("simple/callLambda", "A C++ lambda is exposed to lua and called") {
REQUIRE(x == 1); REQUIRE(x == 1);
} }
TEST_CASE("advanced/callLambdaReturns", "Checks for lambdas returning values") { TEST_CASE("advanced/get_and_call", "Checks for lambdas returning values after a get operation") {
const static std::string lol = "lol", str = "str"; const static std::string lol = "lol", str = "str";
const static std::tuple<int, float, double, std::string> heh_tuple = std::make_tuple(1, 6.28f, 3.14, std::string("heh")); const static std::tuple<int, float, double, std::string> heh_tuple = std::make_tuple(1, 6.28f, 3.14, std::string("heh"));
sol::state lua; sol::state lua;
@ -146,7 +147,42 @@ TEST_CASE("advanced/callLambdaReturns", "Checks for lambdas returning values") {
REQUIRE((lua.get<sol::function>("j").call<int, float, double, std::string>() == heh_tuple)); REQUIRE((lua.get<sol::function>("j").call<int, float, double, std::string>() == heh_tuple));
} }
TEST_CASE("advanced/callLambda2", "A C++ lambda is exposed to lua and called") { TEST_CASE("advanced/operator[]_calls", "Checks for lambdas returning values using operator[]") {
const static std::string lol = "lol", str = "str";
const static std::tuple<int, float, double, std::string> heh_tuple = std::make_tuple(1, 6.28f, 3.14, std::string("heh"));
sol::state lua;
REQUIRE_NOTHROW(lua.set_function("a", [ ] { return 42; }));
REQUIRE(lua["a"].call<int>() == 42);
REQUIRE_NOTHROW(lua.set_function("b", [ ] { return 42u; }));
REQUIRE(lua["b"].call<unsigned int>() == 42u);
REQUIRE_NOTHROW(lua.set_function("c", [ ] { return 3.14; }));
REQUIRE(lua["c"].call<double>() == 3.14);
REQUIRE_NOTHROW(lua.set_function("d", [ ] { return 6.28f; }));
REQUIRE(lua["d"].call<float>() == 6.28f);
REQUIRE_NOTHROW(lua.set_function("e", [ ] { return "lol"; }));
REQUIRE(lua["e"].call<std::string>() == lol);
REQUIRE_NOTHROW(lua.set_function("f", [ ] { return true; }));
REQUIRE(lua["f"].call<bool>());
REQUIRE_NOTHROW(lua.set_function("g", [ ] { return std::string("str"); }));
REQUIRE(lua["g"].call<std::string>() == str);
REQUIRE_NOTHROW(lua.set_function("h", [ ] { }));
REQUIRE_NOTHROW(lua["h"].call());
REQUIRE_NOTHROW(lua.set_function("i", [ ] { return sol::nil; }));
REQUIRE(lua["i"].call<sol::nil_t>() == sol::nil);
REQUIRE_NOTHROW(lua.set_function("j", [ ] { return std::make_tuple(1, 6.28f, 3.14, std::string("heh")); }));
REQUIRE((lua["j"].call<int, float, double, std::string>() == heh_tuple));
}
TEST_CASE("advanced/call_lambdas", "A C++ lambda is exposed to lua and called") {
sol::state lua; sol::state lua;
int x = 0; int x = 0;
@ -159,13 +195,13 @@ TEST_CASE("advanced/callLambda2", "A C++ lambda is exposed to lua and called") {
REQUIRE(x == 9); REQUIRE(x == 9);
} }
TEST_CASE("negative/basicError", "Check if error handling works correctly") { TEST_CASE("negative/basic_errors", "Check if error handling works correctly") {
sol::state lua; sol::state lua;
REQUIRE_THROWS(lua.script("nil[5]")); REQUIRE_THROWS(lua.script("nil[5]"));
} }
TEST_CASE("libraries", "Check if we can open libraries through sol") { TEST_CASE("libraries", "Check if we can open libraries") {
sol::state lua; sol::state lua;
REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base, sol::lib::os)); REQUIRE_NOTHROW(lua.open_libraries(sol::lib::base, sol::lib::os));
} }
@ -236,4 +272,54 @@ TEST_CASE("functions/return_order_and_multi_get", "Check if return order is in t
REQUIRE(tcpp == triple); REQUIRE(tcpp == triple);
REQUIRE(tlua == triple); REQUIRE(tlua == triple);
REQUIRE(tluaget == triple); REQUIRE(tluaget == triple);
}
TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works properly") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script("foo = 20\nbar = \"hello world\"");
// basic retrieval
std::string bar = lua["bar"];
int foo = lua["foo"];
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// test operator= for stringification
bar = lua["bar"];
// basic setting
lua["bar"] = 20.4;
lua["foo"] = "goodbye";
// doesn't modify the actual values obviously.
REQUIRE(bar == "hello world");
REQUIRE(foo == 20);
// function setting
lua["test"] = plop_xyz;
REQUIRE_NOTHROW(lua.script("assert(test(10, 11, \"hello\") == 11)"));
// function retrieval
sol::function test = lua["test"];
REQUIRE(test.call<int>(10, 11, "hello") == 11);
// setting a lambda
lua["lamb"] = [](int x) {
return x * 2;
};
REQUIRE_NOTHROW(lua.script("assert(lamb(220) == 440)"));
// function retrieval of a lambda
sol::function lamb = lua["lamb"];
REQUIRE(lamb.call<int>(220) == 440);
// test const table retrieval
auto assert1 = [](const sol::table& t) {
std::string a = t["foo"];
int b = t["bar"];
std::cout << a << ',' << b << '\n';
};
REQUIRE_NOTHROW(assert1(lua.global_table()));
} }