all tests compile excepted related to test_table_return_two(), which uses contiguous container std::vector but has key-value pairs inside of it

c++ semantics dictate that it's accessed by index, but the tests seem to want to indicate that it should be accessed like a hashmap (or just using basic lua table semantics)
i have no idea how to make this incompatibility work in the new system...
i will ask repo master if he knows anything
This commit is contained in:
PrincessNyanara 2014-06-07 16:33:39 -04:00 committed by Rapptz
parent 6712ebe0bd
commit 423e44d6dc
7 changed files with 205 additions and 63 deletions

View File

@ -5,10 +5,10 @@
namespace sol {
template <typename Tc>
template <typename Tc, typename = void>
struct container {
typedef typename std::conditional<std::is_lvalue_reference<Tc>::value, Tc, Decay<Tc>>::type T;
typedef Decay<decltype(*(std::declval<T>().begin()))> value_type;
typedef Unqualified<decltype(*(std::declval<T>().begin()))> value_type;
T cont;
template <typename... Args>
@ -20,26 +20,54 @@ struct container {
return cont;
}
void set( std::ptrdiff_t i, const value_type& value ) {
void set(std::ptrdiff_t i, const value_type& value) {
cont[ i ] = value;
}
value_type& get( std::ptrdiff_t i ) {
value_type& get(std::ptrdiff_t i) {
return cont[ i ];
}
std::size_t size( ) const {
std::size_t size() const {
return cont.size();
}
};
template <typename Tc>
struct container<Tc, typename std::enable_if<has_key_value_pair<Unqualified<Tc>>::value>::type> {
typedef typename std::conditional<std::is_lvalue_reference<Tc>::value, Tc, Decay<Tc>>::type T;
typedef Unqualified<decltype((*(std::declval<T>().begin())).first)> key_type;
typedef Unqualified<decltype((*(std::declval<T>().begin())).second)> value_type;
T cont;
template <typename... Args>
container (Args&&... args) : cont(std::forward<Args>(args)...){
}
operator T& () const {
return cont;
}
void set(key_type i, const value_type& value) {
cont[ i ] = value;
}
value_type& get(key_type i) {
return cont.at(i);
}
std::size_t size() const {
return cont.size();
}
};
namespace stack {
template<typename T>
struct pusher<T, typename std::enable_if<has_begin_end<T>::value>::type> {
template<typename U = T, EnableIf<Not<has_key_value_pair<U>>> = 0>
static void push(lua_State* L, const T& cont) {
typedef container<U> container_t;
typedef container<T> container_t;
// todo: NEED to find a different way of handling this...
static std::vector<std::shared_ptr<void>> classes{};
userdata<container_t> classdata(default_constructor,
@ -55,19 +83,22 @@ struct pusher<T, typename std::enable_if<has_begin_end<T>::value>::type> {
std::allocator<container_t> alloc{};
alloc.construct(c, cont);
auto&& meta = userdata_traits<T>::metatable;
luaL_getmetatable(L, std::addressof(meta[0]));
auto&& meta = userdata_traits<container_t>::metatable;
if (luaL_newmetatable(L, std::addressof(meta[0])) == 1) {
std::string err("metatable not defined for ");
err += meta;
throw error(err);
}
lua_setmetatable(L, -2);
}
};
template<typename U = T, EnableIf<has_key_value_pair<U>> = 0>
static void push(lua_State* L, const T& cont) {
lua_createtable(L, cont.size(), 0);
for(auto&& pair : cont) {
pusher<Unqualified<decltype(pair.first)>>::push(L, pair.first);
pusher<Unqualified<decltype(pair.second)>>::push(L, pair.second);
lua_settable(L, -3);
}
template<typename T>
struct getter<T, typename std::enable_if<has_begin_end<T>::value>::type> {
static Unqualified<T>& get(lua_State* L, int index = -1) {
typedef container<Unqualified<T>> container_t;
container_t& data = stack::get<container_t>(L, index);
return data.cont;
}
};
} // stack

View File

@ -79,7 +79,7 @@ public:
template<typename... Ret, typename... Args>
typename return_type<Ret...>::type call(Args&&... args) const {
push();
stack::push(state(), std::forward<Args>(args)...);
stack::push_args(state(), std::forward<Args>(args)...);
return invoke(types<Ret...>(), sizeof...(Args));
}
};
@ -166,7 +166,7 @@ struct pusher<function_t> {
lua_settable(L, -3);
}
stack::detail::push_userdata(L, userdata, metatablename);
stack::detail::push_userdata<void*>(L, metatablename, userdata);
stack::pusher<lua_CFunction>{}.push(L, freefunc, 1);
}

View File

@ -288,7 +288,7 @@ struct userdata_function : public base_function {
template<typename Return, typename Raw = Unqualified<Return>>
typename std::enable_if<std::is_same<T, Raw>::value, void>::type push(lua_State* L, Return&& r) {
if ( detail::get_ptr(r) == fx.item ) {
if (detail::get_ptr(r) == fx.item) {
// push nothing
// note that pushing nothing with the ':'
// syntax means we leave the instance of what
@ -331,6 +331,8 @@ struct userdata_function : public base_function {
virtual int operator()(lua_State* L) override {
fx.item = detail::get_ptr(stack::get<Tp>(L, 1));
if (fx.item == nullptr)
throw error("userdata for function call is null: are you using wrong call syntax? (use item:function(...) synax)");
return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L);
}
};

View File

@ -47,10 +47,11 @@ T* get_ptr(T* val) {
namespace stack {
namespace detail {
template<typename T, typename Key, typename U = Unqualified<T>>
inline void push_userdata(lua_State* L, T&& userdata, Key&& metatablekey) {
U* pdatum = static_cast<U*>(lua_newuserdata(L, sizeof(U)));
new(pdatum)U(std::forward<T>(userdata));
template<typename T, typename Key, typename... Args>
inline void push_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
T* pdatum = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
std::allocator<T> alloc{};
alloc.construct(pdatum, std::forward<Args>(args)...);
luaL_getmetatable(L, std::addressof(metatablekey[0]));
lua_setmetatable(L, -2);
}
@ -193,16 +194,53 @@ struct pusher {
pusher<T*>{}.push(L, std::addressof(t));
}
template<typename U = T, EnableIf<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<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) {
detail::push_userdata(L, std::move(t), userdata_traits<T*>::metatable);
detail::push_userdata<U>(L, userdata_traits<T*>::metatable, std::move(t));
}
};
template<typename T>
struct pusher<T*> {
static void push(lua_State* L, T* obj) {
detail::push_userdata(L, obj, userdata_traits<T*>::metatable);
detail::push_userdata<T*>(L, userdata_traits<T*>::metatable, obj);
}
};
template <>
struct pusher<table> {
template<typename T, typename U = Unqualified<T>, EnableIf<std::is_base_of<reference, U>> = 0>
static void push_as(lua_State *L, T&& ref) {
ref.push();
}
template<typename T, typename U = Unqualified<T>, EnableIf<has_begin_end<U>, Not<has_key_value_pair<U>>> = 0>
static void push_as(lua_State* L, const T& cont) {
lua_createtable(L, cont.size(), 0);
unsigned index = 1;
for(auto&& i : cont) {
// push the index
pusher<unsigned>{}.push(L, index++);
// push the value
pusher<Unqualified<decltype(i)>>{}.push(L, i);
// set the table
lua_settable(L, -3);
}
}
template<typename T, typename U = Unqualified<T>, EnableIf<has_begin_end<U>, has_key_value_pair<U>> = 0>
static void push_as(lua_State* L, const T& cont) {
lua_createtable(L, cont.size(), 0);
for(auto&& pair : cont) {
pusher<Unqualified<decltype(pair.first)>>{}.push(L, pair.first);
pusher<Unqualified<decltype(pair.second)>>{}.push(L, pair.second);
lua_settable(L, -3);
}
}
template <typename T>
static void push(lua_State *L, T&& table) {
push_as(L, std::forward<T>(table));
}
};
@ -284,8 +322,17 @@ inline void push(lua_State*) {
template<typename T, typename... Args>
inline void push(lua_State* L, T&& t, Args&&... args) {
using swallow = char[];
pusher<Unqualified<T>>{}.push(L, std::forward<T>(t), std::forward<Args>(args)...);
}
inline void push_args(lua_State*) {
// do nothing
}
template<typename T, typename... Args>
inline void push_args(lua_State* L, T&& t, Args&&... args) {
pusher<Unqualified<T>>{}.push(L, std::forward<T>(t));
using swallow = char[];
void(swallow{'\0', (pusher<Unqualified<Args>>{}.push(L, std::forward<Args>(args)), '\0')... });
}

View File

@ -232,7 +232,7 @@ private:
}
push();
stack::detail::push_userdata(state(), userdata, metatablename);
stack::detail::push_userdata<void*>(state(), metatablename, userdata);
luaL_setfuncs(state(), funcreg, 1);
pop();
return *this;

View File

@ -26,6 +26,8 @@
#include "function_types.hpp"
#include "userdata_traits.hpp"
#include <vector>
#include <array>
#include <algorithm>
namespace sol {
namespace detail {
@ -38,12 +40,17 @@ inline std::unique_ptr<T> make_unique(Args&&... args) {
template<typename T>
class userdata {
private:
const static std::array<std::string, 19> metafunctionnames;
std::string luaname;
std::vector<std::string> functionnames;
std::vector<std::unique_ptr<base_function>> funcs;
std::vector<std::unique_ptr<base_function>> ptrfuncs;
std::vector<std::unique_ptr<base_function>> metafuncs;
std::vector<std::unique_ptr<base_function>> ptrmetafuncs;
std::vector<luaL_Reg> functiontable;
std::vector<luaL_Reg> ptrfunctiontable;
std::vector<luaL_Reg> metafunctiontable;
std::vector<luaL_Reg> ptrmetafunctiontable;
template<typename... TTypes>
struct constructor {
@ -79,7 +86,7 @@ private:
match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), typename std::common_type<TTypes>::type()...);
luaL_getmetatable(L, std::addressof(meta[0]));
lua_setmetatable(L, -2);
lua_setmetatable(L, -1);
return 1;
}
@ -100,14 +107,24 @@ private:
void build_function_tables() {}
template<std::size_t N, typename... Args, typename TBase, typename Ret>
void build_function_tables(std::string name, Ret TBase::* func, Args&&... args) {
void build_function_tables(std::string funcname, Ret TBase::* func, Args&&... args) {
static_assert(std::is_base_of<TBase, T>::value, "Any registered function must be part of the class");
typedef typename std::decay<decltype(func)>::type function_type;
functionnames.push_back(std::move(name));
funcs.emplace_back(detail::make_unique<userdata_function<function_type, T>>(std::move(func)));
ptrfuncs.emplace_back(detail::make_unique<userdata_function<function_type, typename std::add_pointer<T>::type>>(std::move(func)));
functiontable.push_back({ functionnames.back().c_str(), &base_function::userdata<N>::call });
ptrfunctiontable.push_back({ functionnames.back().c_str(), &base_function::userdata<N>::call });
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
auto metamethod = std::find(metafunctionnames.begin(), metafunctionnames.end(), name);
if (metamethod != metafunctionnames.end()) {
metafuncs.emplace_back(detail::make_unique<userdata_function<function_type, T>>(std::move(func)));
ptrmetafuncs.emplace_back(detail::make_unique<userdata_function<function_type, typename std::add_pointer<T>::type>>(std::move(func)));
metafunctiontable.push_back({ name.c_str(), &base_function::userdata<N>::call });
ptrmetafunctiontable.push_back({ name.c_str(), &base_function::userdata<N>::call });
}
else {
funcs.emplace_back(detail::make_unique<userdata_function<function_type, T>>(std::move(func)));
ptrfuncs.emplace_back(detail::make_unique<userdata_function<function_type, typename std::add_pointer<T>::type>>(std::move(func)));
functiontable.push_back({ name.c_str(), &base_function::userdata<N>::call });
ptrfunctiontable.push_back({ name.c_str(), &base_function::userdata<N>::call });
}
build_function_tables<N + 1>(std::forward<Args>(args)...);
}
@ -123,21 +140,27 @@ public:
functionnames.reserve(sizeof...(args) + 2);
functiontable.reserve(sizeof...(args) + 2);
ptrfunctiontable.reserve(sizeof...(args) + 2);
metafunctiontable.reserve(sizeof...(args) + 2);
ptrmetafunctiontable.reserve(sizeof...(args) + 2);
funcs.reserve(sizeof...(args) + 2);
ptrfuncs.reserve(sizeof...(args) + 2);
metafuncs.reserve(sizeof...(args) + 2);
ptrmetafuncs.reserve(sizeof...(args) + 2);
build_function_tables<0>(std::forward<Args>(args)...);
functionnames.push_back("new");
functiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
functionnames.push_back("__gc");
functiontable.push_back({ functionnames.back().c_str(), &destructor<sizeof...(Args) / 2>::destruct });
metafunctiontable.push_back({ functionnames.back().c_str(), &destructor<sizeof...(Args) / 2>::destruct });
// ptr_functions does not participate in garbage collection/new,
// as all pointered types are considered
// to be references. This makes returns of
// `std::vector<int>&` and `std::vector<int>*` work
functiontable.push_back({ nullptr, nullptr });
metafunctiontable.push_back({ nullptr, nullptr });
ptrfunctiontable.push_back({ nullptr, nullptr });
ptrmetafunctiontable.push_back({ nullptr, nullptr });
}
template<typename... Args, typename... CArgs>
@ -156,6 +179,14 @@ public:
return ptrfuncs;
}
const std::vector<std::unique_ptr<base_function>>& meta_functions () const {
return metafuncs;
}
const std::vector<std::unique_ptr<base_function>>& meta_reference_functions () const {
return ptrmetafuncs;
}
const std::vector<luaL_Reg>& function_table () const {
return functiontable;
}
@ -164,34 +195,65 @@ public:
return ptrfunctiontable;
}
const std::vector<luaL_Reg>& meta_function_table () const {
return metafunctiontable;
}
const std::vector<luaL_Reg>& meta_reference_function_table () const {
return ptrmetafunctiontable;
}
const std::string& name () const {
return luaname;
}
};
template <typename T>
const std::array<std::string, 19> userdata<T>::metafunctionnames = {
"__index",
"__newindex",
"__mode",
"__call",
"__metatable",
"__tostring",
"__len",
"__gc",
"__unm",
"__add",
"__sub",
"__mul",
"__div",
"__mod",
"__pow",
"__concat",
"__eq",
"__lt",
"__le",
};
namespace stack {
template <typename T>
struct pusher<userdata<T>> {
static void push ( lua_State* L, userdata<T>& user ) {
auto&& ptrmeta = userdata_traits<typename std::add_pointer<T>::type>::metatable;
luaL_newmetatable(L, ptrmeta.c_str());
for (std::size_t upvalues = 0; upvalues < user.reference_functions().size(); ++upvalues) {
stack::push(L, static_cast<void*>(user.reference_functions()[ upvalues ].get()));
}
luaL_setfuncs(L, user.reference_function_table().data(), static_cast<uint32_t>(user.reference_functions().size()));
template <typename Meta, typename Funcs, typename FuncTable, typename MetaFuncs, typename MetaFuncTable>
static void push_metatable(lua_State* L, Meta&& meta, Funcs&& funcs, FuncTable&& functable, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) {
luaL_newmetatable(L, std::addressof(meta[0]));
// regular functions accessed through __index semantics
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
auto&& meta = userdata_traits<T>::metatable;
luaL_newmetatable(L, meta.c_str());
for (std::size_t upvalues = 0; upvalues < user.functions().size(); ++upvalues) {
stack::push(L, static_cast<void*>(user.functions()[ upvalues ].get()));
for (std::size_t u = 0; u < funcs.size(); ++u) {
stack::push<upvalue_t>(L, funcs[ u ].get());
}
luaL_setfuncs(L, user.function_table().data(), static_cast<uint32_t>(user.functions().size()));
luaL_setfuncs(L, functable.data(), static_cast<uint32_t>(funcs.size()));
// meta functions
for (std::size_t u = 0; u < metafuncs.size(); ++u) {
stack::push<upvalue_t>(L, metafuncs[ u ].get());
}
luaL_setfuncs(L, metafunctable.data(), static_cast<uint32_t>(metafuncs.size()));
}
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
static void push (lua_State* L, userdata<T>& user) {
push_metatable(L, userdata_traits<T>::metatable, user.functions(), user.function_table(), user.meta_functions(), user.meta_function_table());
push_metatable(L, userdata_traits<T*>::metatable, user.reference_functions(), user.reference_function_table(), user.meta_reference_functions(), user.meta_reference_function_table());
}
};
} // stack

View File

@ -14,14 +14,14 @@ void test_free_func2(std::function<int(int)> f, int arg1) {
throw sol::error("failed function call!");
}
std::function<int()> makefn () {
std::function<int()> makefn() {
auto fx = []() -> int {
return 0x1456789;
};
return fx;
}
void takefn ( std::function<int()> purr ) {
void takefn(std::function<int()> purr) {
if (purr() != 0x1456789)
throw 0;
}
@ -332,7 +332,7 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work
std::cout << "stateless lambda()" << std::endl;
return "test";
}
);
);
REQUIRE_NOTHROW(run_script(lua));
lua.get<sol::table>("os").set_function("fun", &free_function);
@ -389,7 +389,7 @@ TEST_CASE("functions/sol::function to std::function", "check if conversion to st
lua.set_function("testFunc2", test_free_func2);
lua.script(
"testFunc(function() print(\"hello std::function\") end)"
);
);
lua.script(
"function m(a)\n"
" print(\"hello std::function with arg \", a)\n"
@ -397,18 +397,18 @@ TEST_CASE("functions/sol::function to std::function", "check if conversion to st
"end\n"
"\n"
"testFunc2(m, 1)"
);
);
}
TEST_CASE("functions/returning functions from C++ and getting in lua", "check to see if returning a functor and getting a functor from lua is possible") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function( "makefn", makefn );
lua.set_function( "takefn", takefn );
lua.script( "afx = makefn()\n"
lua.set_function("makefn", makefn);
lua.set_function("takefn", takefn);
lua.script("afx = makefn()\n"
"print(afx())\n"
"takefn(afx)\n" );
"takefn(afx)\n");
}
TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works properly") {
@ -591,7 +591,7 @@ TEST_CASE("tables/self-referential userdata", "userdata classes must play nice w
"local a = test.new()\n"
"a:g(\"woof\")\n"
"a:f(a)\n"
);
);
}
TEST_CASE("tables/arbitrary-creation", "tables should be created from standard containers") {