Class binding now works with multiple functions

Refactoring on function_types.hpp performed to slim down some of the calls: could use more refactoring
Drastically simplified userdata's binding capabilities: constructor supports both `:` and `.` syntax (but member functions DO NOT).
All tests are passing
This commit is contained in:
ThePhD 2014-04-26 18:23:56 -04:00
parent 4d6d39be88
commit 0088002abf
10 changed files with 218 additions and 149 deletions

View File

@ -35,19 +35,19 @@ namespace detail {
std::string demangle(const std::type_info& id) {
const static std::array<std::string, 2> removals = { "struct ", "class " };
const static std::array<std::string, 2> replacements = { "::", "_" };
std::string realname = id.name( );
std::string realname = id.name();
for(std::size_t r = 0; r < removals.size(); ++r) {
auto found = realname.find(removals[r]);
while (found != std::string::npos) {
while (found != std::string::npos) {
realname.erase(found, removals[r].size());
found = realname.find( removals[r] );
}
found = realname.find(removals[r]);
}
}
for(std::size_t r = 0; r < replacements.size(); r+=2) {
auto found = realname.find(replacements[r]);
while (found != std::string::npos) {
while (found != std::string::npos) {
realname.replace(found, replacements[r].size(), replacements[r+1]);
found = realname.find(replacements[r], found);
found = realname.find(replacements[r], found);
}
}
return realname;

View File

@ -104,26 +104,41 @@ struct static_member_function {
};
struct base_function {
static int call(lua_State* L) {
void** pinheritancedata = static_cast<void**>(stack::get<lightuserdata_t>(L, 1).value);
void* inheritancedata = *pinheritancedata;
if (inheritancedata == nullptr) {
static int base_call(lua_State* L, void* inheritancedata) {
if (inheritancedata == nullptr)
throw sol_error("call from Lua to C++ function has null data");
}
base_function* pfx = static_cast<base_function*>(inheritancedata);
base_function& fx = *pfx;
int r = fx(L);
return r;
}
static int gc(lua_State* L) {
void** puserdata = static_cast<void**>(stack::get<userdata_t>(L, 1).value);
void* userdata = *puserdata;
base_function* ptr = static_cast<base_function*>(userdata);
static int base_gc(lua_State* L, void* udata) {
if (udata == nullptr)
throw sol_error("call from lua to C++ gc function with null data");
base_function* ptr = static_cast<base_function*>(udata);
std::default_delete<base_function> dx{};
dx(ptr);
return 0;
}
static int call(lua_State* L) {
void** pinheritancedata = static_cast<void**>(stack::get<lightuserdata_t>(L, 1).value);
return base_call(L, *pinheritancedata);
}
static int gc(lua_State* L) {
void** pudata = static_cast<void**>(stack::get<userdata_t>(L, 1).value);
return base_gc(L, *pudata);
}
template<std::size_t i>
struct userdata {
static int call(lua_State* L) {
// Zero-based template parameter, but upvalues start at 1
return base_call(L, stack::get<lightuserdata_t>(L, i + 1));
}
};
virtual int operator()(lua_State*) {
throw sol_error("failure to call specialized wrapped C++ function from Lua");
@ -220,9 +235,11 @@ struct userdata_function : public base_function {
template<typename... FxArgs>
functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward<FxArgs>(fxargs)...) {}
void pre_call(lua_State* L) {
void* userdata = lua_touserdata(L, 0);
item = static_cast<T*>(userdata);
void prepare(lua_State* L) {
void* udata = stack::get<userdata_t>(L, 1);
if (udata == nullptr)
throw sol_error("must use the syntax [object]:[function] to call member functions bound to C++");
item = static_cast<T*>(udata);
}
template<typename... Args>
@ -255,7 +272,7 @@ struct userdata_function : public base_function {
}
virtual int operator()(lua_State* L) override {
fx.pre_call(L);
fx.prepare(L);
return (*this)(tuple_types<typename traits_type::return_type>(), typename traits_type::args_type(), L);
}
};

View File

@ -24,7 +24,7 @@
#include "function.hpp"
namespace sol {
template <typename Table, typename Key>
template<typename Table, typename Key>
struct proxy {
private:
Table& tbl;
@ -32,7 +32,7 @@ private:
public:
template <typename T>
template<typename T>
proxy(Table& table, T&& key) : tbl(table), key(std::forward<T>(key)) {
}
@ -80,22 +80,22 @@ public:
return get<std::string>();
}
template <typename T = void>
template<typename T = void>
operator bool() const {
return get<bool>();
}
template <typename T = void>
template<typename T = void>
operator double() const {
return get<double>();
}
template <typename T = void>
template<typename T = void>
operator float() const {
return get<float>();
}
template <typename T = void>
template<typename T = void>
operator int() const {
return get<int>();
}
@ -106,22 +106,22 @@ public:
}
};
template <typename Table, typename Key, typename T>
template<typename Table, typename Key, typename T>
inline bool operator== (T&& left, const proxy<Table, Key>& right) {
return left == right.template get<Decay<T>>();
}
template <typename Table, typename Key, typename T>
template<typename Table, typename Key, typename T>
inline bool operator== (const proxy<Table, Key>& right, T&& left) {
return right.template get<Decay<T>>() == left;
}
template <typename Table, typename Key, typename T>
template<typename Table, typename Key, typename T>
inline bool operator!= (T&& left, const proxy<Table, Key>& right) {
return right.template get<Decay<T>>() != left;
}
template <typename Table, typename Key, typename T>
template<typename Table, typename Key, typename T>
inline bool operator!= (const proxy<Table, Key>& right, T&& left) {
return right.template get<Decay<T>>() != left;
}

View File

@ -134,7 +134,12 @@ inline const char* get<const char*>(lua_State* L, int index) {
return lua_tostring(L, index);
}
template <typename T>
template<>
inline type get<type>(lua_State* L, int index) {
return static_cast<type>(lua_type(L, index));
}
template<typename T>
inline std::pair<T, int> get_user(lua_State* L, int index = 1) {
const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
@ -234,6 +239,15 @@ inline void push_tuple(lua_State* L, indices<I...>, T&& tuplen) {
swallow {'\0', (sol::stack::push(L, std::get<I>(tuplen)), '\0')... };
}
template<typename F, typename... Vs, typename... Args>
inline auto ltr_get(lua_State*, int index, F&& f, types<Args...>, types<>, Vs&&... vs) -> decltype(f(std::forward<Vs>(vs)...)) {
return f(std::forward<Vs>(vs)...);
}
template<typename F, typename Head, typename... Tail, typename... Vs, typename... Args>
inline auto ltr_get(lua_State* L, int index, F&& f, types<Args...> t, types<Head, Tail...>, Vs&&... vs) -> decltype(f(std::declval<Args>()...)) {
return ltr_get(L, index + 1, std::forward<F>(f), t, types<Tail...>(), std::forward<Vs>(vs)..., get<Head>(L, index));
}
template<typename F, typename... Vs, typename... Args>
inline auto ltr_pop(lua_State*, F&& f, types<Args...>, types<>, Vs&&... vs) -> decltype(f(std::forward<Vs>(vs)...)) {
return f(std::forward<Vs>(vs)...);
@ -278,6 +292,11 @@ inline void push_reverse(lua_State* L, std::tuple<Args...>&& tuplen) {
detail::push_tuple(L, build_reverse_indices<sizeof...(Args)>(), std::move(tuplen));
}
template<typename... Args, typename TFx>
inline auto get_call(lua_State* L, int index, TFx&& fx, types<Args...> t) -> decltype(detail::ltr_get(L, index, std::forward<TFx>(fx), t, t)) {
return detail::ltr_get(L, index, std::forward<TFx>(fx), t, t);
}
template<typename... Args, typename TFx>
inline auto pop_call(lua_State* L, TFx&& fx, types<Args...> t) -> decltype(detail::ltr_pop(L, std::forward<TFx>(fx), t, t)) {
return detail::ltr_pop(L, std::forward<TFx>(fx), t, t);
@ -298,6 +317,27 @@ inline void push_args(lua_State* L, Arg&& arg, Args&&... args) {
stack::push(L, std::forward<Arg>(arg));
void(swallow{'\0', (stack::push(L, std::forward<Args>(args)), '\0')... });
}
inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) {
if (get<type>(L, 1) == type::table) {
if (luaL_newmetatable(L, meta.c_str()) == 0) {
lua_settop(L, -2);
return call_syntax::colon;
}
}
return call_syntax::dot;
}
inline std::string dump_types(lua_State* L) {
std::string visual;
std::size_t size = lua_gettop(L) + 1;
for (std::size_t i = 1; i < size; ++i) {
if (i != 1)
visual += " | ";
visual += type_name(L, stack::get<type>(L, i));
}
return visual;
}
} // stack
} // sol

View File

@ -180,12 +180,12 @@ public:
return reg;
}
template <typename T>
template<typename T>
proxy<table, T> operator[](T&& key) {
return global[std::forward<T>(key)];
}
template <typename T>
template<typename T>
proxy<const table, T> operator[](T&& key) const {
return global[std::forward<T>(key)];
}

View File

@ -113,43 +113,19 @@ public:
std::forward<T>(key), std::forward<TFx>(fx), std::forward<TObj>(obj));
}
template <typename T>
template<typename T>
table& set_class(userdata<T>& user) {
push();
lua_createtable(state(), 0, 0);
int classid = lua_gettop(state());
// Register metatable for user data in registry
// using the metaname key generated from the demangled name
luaL_newmetatable(state(), user.meta.c_str());
int metaid = lua_gettop(state());
// Meta functions: have no light up values
luaL_setfuncs(state(), user.metatable.data(), 0);
// Regular functions: each one references an upvalue at its own index,
// resulting in [function count] upvalues
//luaL_newlib(L, functiontable.data());
// the newlib macro doesn't have a form for when you need upvalues:
// we duplicate the work below
lua_createtable(state(), 0, user.functiontable.size() - 1);
for (std::size_t upvalues = 0; upvalues < user.functions.size(); ++upvalues) {
stack::push(state(), static_cast<void*>(user.functions[ upvalues ].get()));
}
luaL_setfuncs(state(), user.functiontable.data(), static_cast<uint32_t>(user.functions.size()));
lua_setfield(state(), metaid, "__index");
// Meta functions: no upvalues
lua_createtable(state(), 0, user.metatable.size() - 1);
luaL_setfuncs(state(), user.metatable.data(), 0); // 0, for no upvalues
lua_setfield(state(), metaid, "__metatable");
lua_setmetatable(state(), classid);
lua_pushvalue(state(), -1);
lua_setfield(state(), -1, "__index");
lua_setglobal(state(), user.luaname.c_str());
pop();
return *this;
}
@ -158,12 +134,12 @@ public:
return lua_rawlen(state(), -1);
}
template <typename T>
template<typename T>
proxy<table, T> operator[](T&& key) {
return proxy<table, T>(*this, std::forward<T>(key));
}
template <typename T>
template<typename T>
proxy<const table, T> operator[](T&& key) const {
return proxy<const table, T>(*this, std::forward<T>(key));
}

View File

@ -74,9 +74,11 @@ struct tuple_types : types<Args...>, std::false_type {};
template<typename... Args>
struct tuple_types<std::tuple<Args...>> : types<Args...>, std::true_type {};
template <typename... Tn>
template<typename... Tn>
struct constructors {};
const auto default_constructor = constructors<types<>>{};
} // sol
#endif // SOL_TUPLE_HPP

View File

@ -43,6 +43,11 @@ struct userdata_t {
operator void* () const { return value; }
};
enum class call_syntax {
dot = 0,
colon = 1
};
enum class type : int {
none = LUA_TNONE,
nil = LUA_TNIL,
@ -69,7 +74,11 @@ inline void type_assert(lua_State* L, int index, type expected) {
}
}
template <typename T>
inline std::string type_name(lua_State*L, type t) {
return lua_typename(L, static_cast<int>(t));
}
template<typename T>
class userdata;
class table;
class function;

View File

@ -35,7 +35,7 @@ inline std::unique_ptr<T> make_unique(Args&&... args) {
}
} // detail
template <typename T>
template<typename T>
class userdata {
private:
friend table;
@ -46,142 +46,105 @@ private:
std::vector<std::string> functionnames;
std::vector<std::unique_ptr<base_function>> functions;
std::vector<luaL_Reg> functiontable;
std::vector<luaL_Reg> metatable;
template <typename... TTypes>
template<typename... TTypes>
struct constructor {
template <typename... Args>
static void do_constructor(lua_State* L, T* obj, int argcount, types<Args...> t) {
auto fx = [&obj] ( Args&&... args ) -> void {
template<typename... Args>
static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types<Args...>) {
auto fx = [&obj] (Args&&... args) -> void {
std::allocator<T> alloc{};
alloc.construct(obj, std::forward<Args>(args)...);
};
stack::pop_call(L, fx, t);
}
stack::get_call(L, 1 + static_cast<int>(syntax), fx, types<Args...>());
}
static void match_constructor(lua_State* L, T* obj, int argcount) {
if (argcount != 0)
throw sol_error("No matching constructor for the arguments provided");
static void match_constructor(lua_State* L, T* obj, call_syntax, int argcount) {
throw sol_error("No matching constructor for the arguments provided");
}
template<typename ...CArgs, typename... Args>
static void match_constructor(lua_State* L, T* obj, int argcount, types<CArgs...> t, Args&&... args) {
template<typename ...CArgs, typename... Args>
static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types<CArgs...> t, Args&&... args) {
if (argcount == sizeof...(CArgs)) {
do_constructor( L, obj, argcount, t );
do_constructor(L, obj, syntax, argcount, t);
return;
}
match_constructor( L, obj, argcount, std::forward<Args>(args)...);
match_constructor(L, obj, syntax, argcount, std::forward<Args>(args)...);
}
static int construct(lua_State* L) {
call_syntax syntax = stack::get_call_syntax(L, meta);
int argcount = lua_gettop(L);
// First argument is now a table that represent the class to instantiate
luaL_checktype( L, 1, LUA_TTABLE );
// Table represents the instance
lua_createtable(L, 0, 0);
// Set first argument of new to metatable of instance
lua_pushvalue(L, 1);
void* udata = lua_newuserdata(L, sizeof(T));
T* obj = static_cast<T*>(udata);
match_constructor(L, obj, syntax,
argcount - static_cast<int>(syntax),
std::common_type<TTypes>::type()...);
luaL_getmetatable(L, meta.c_str());
lua_setmetatable(L, -2);
// Do function lookups in metatable
lua_pushvalue(L, 1);
lua_setfield(L, 1, "__index");
void* udata = lua_newuserdata( L, sizeof( T ) );
T* obj = static_cast<T*>( udata );
match_constructor( L, obj, argcount - 1, std::common_type<TTypes>::type( )... );
//match_constructor( L, obj, argcount - 1, types<int>( ) );
luaL_getmetatable(L, meta.c_str());
lua_setmetatable(L, -2);
lua_setfield(L, -2, "__self");
return 1;
return 1;
}
};
template<std::size_t n>
struct destructor {
static int destruct(lua_State* L) {
for(std::size_t i = 0; i < n; ++i) {
lightuserdata_t ludata = stack::get<lightuserdata_t>(L, i);
lua_func* func = static_cast<lua_func*>(ludata.value);
std::default_delete<lua_func> dx{};
dx(func);
}
userdata_t udata = stack::get<userdata_t>(L, 0);
/*for(std::size_t i = 0; i < n; ++i) {
base_function::base_gc(L, stack::get<lightuserdata_t>(L, i + 1));
}*/
userdata_t udata = stack::get<userdata_t>(L, 1);
T* obj = static_cast<T*>(udata.value);
std::allocator<T> alloc{};
alloc.destroy(obj);
return 0;
}
};
template <std::size_t i>
struct class_func {
static int call(lua_State* L) {
// Zero-based template parameter, but upvalues start at 1
void* inheritancedata = stack::get<lightuserdata_t>(L, i + 1);
if (inheritancedata == nullptr)
throw sol_error("call from Lua to C++ function has null data");
base_function* pfx = static_cast<base_function*>(inheritancedata);
base_function& fx = *pfx;
int r = fx(L);
return r;
}
};
template <std::size_t n>
template<std::size_t n>
void build_function_tables() {
}
template <std::size_t n, typename... Args, typename Ret, typename... MArgs>
template<std::size_t n, typename... Args, typename Ret, typename... MArgs>
void build_function_tables(Ret(T::* func)(MArgs...), std::string name, Args&&... args) {
typedef typename std::decay<decltype(func)>::type fx_t;
functionnames.push_back(std::move(name));
functions.emplace_back(detail::make_unique<userdata_function<fx_t, T>>(std::move(func)));
functiontable.push_back({ functionnames.back().c_str(), &class_func<n>::call });
functiontable.push_back({ functionnames.back().c_str(), &base_function::userdata<n>::call });
build_function_tables<n + 1>(std::forward<Args>(args)...);
}
public:
template <typename... Args>
userdata(Args&&... args) : userdata(classname, std::forward<Args>(args)...) {}
template<typename... Args>
userdata(Args&&... args) : userdata(classname, default_constructor, std::forward<Args>(args)...) {}
template <typename... Args>
userdata(std::string name, Args&&... args) : userdata(name, constructors<types<>>(), std::forward<Args>(args)...) {}
template <typename... Args, typename... CArgs>
template<typename... Args, typename... CArgs>
userdata(constructors<CArgs...> c, Args&&... args) : userdata(classname, std::move(c), std::forward<Args>(args)...) {}
template <typename... Args, typename... CArgs>
template<typename... Args, typename... CArgs>
userdata(std::string name, constructors<CArgs...>, Args&&... args) : luaname(std::move(name)) {
functionnames.reserve(sizeof...(args));
functiontable.reserve(sizeof...(args));
functions.reserve(sizeof...(args));
metatable.reserve(sizeof...(args));
functionnames.reserve(sizeof...(args) + 2);
functiontable.reserve(sizeof...(args) + 3);
functions.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 });
functiontable.push_back({ nullptr, nullptr });
metatable.push_back({ "__gc", &destructor<sizeof...(Args) / 2>::destruct });
metatable.push_back({ nullptr, nullptr });
}
void register_into(const table& s) { }
};
template <typename T>
template<typename T>
const std::string userdata<T>::classname = detail::demangle(typeid(T));
template <typename T>
template<typename T>
const std::string userdata<T>::meta = std::string("sol.stateful.").append(classname);
}

View File

@ -36,6 +36,24 @@ struct fuser {
}
};
namespace crapola {
struct fuser {
int x;
fuser( ) : x( 0 ) {
}
fuser( int x ) : x( x ) {
}
fuser( int x, int x2 ) : x( x * x2 ) {
}
int add( int y ) {
return x + y;
}
int add2( int y ) {
return x + y + 2;
}
};
}
int plop_xyz(int x, int y, std::string z) {
std::cout << x << " " << y << " " << z << std::endl;
return 11;
@ -359,15 +377,59 @@ TEST_CASE("tables/userdata", "Show that we can create classes from userdata and
sol::object a = lua.get<sol::object>("a");
sol::object b = lua.get<sol::object>("b");
sol::object c = lua.get<sol::object>("c");
REQUIRE((a.is<sol::table>()));
REQUIRE((a.is<sol::userdata_t>()));
auto atype = a.get_type();
auto btype = b.get_type();
auto ctype = c.get_type();
REQUIRE((atype == sol::type::table));
REQUIRE((atype == sol::type::userdata));
REQUIRE((btype == sol::type::number));
REQUIRE((ctype == sol::type::number));
int bresult = b.as<int>();
int cresult = c.as<int>();
REQUIRE(bresult == 1);
REQUIRE(cresult == 3);
}
TEST_CASE("tables/userdata constructors", "Show that we can create classes from userdata and use them with multiple destructors") {
sol::state lua;
sol::constructors<sol::types<>, sol::types<int>, sol::types<int, int>> con;
sol::userdata<crapola::fuser> lc(con, &crapola::fuser::add, "add", &crapola::fuser::add2, "add2");
lua.set_class(lc);
lua.script(
"a = crapola_fuser.new(2)\n"
"u = a:add(1)\n"
"v = a:add2(1)\n"
"b = crapola_fuser:new()\n"
"w = b:add(1)\n"
"x = b:add2(1)\n"
"c = crapola_fuser.new(2, 3)\n"
"y = c:add(1)\n"
"z = c:add2(1)\n"
);
sol::object a = lua.get<sol::object>("a");
auto atype = a.get_type();
REQUIRE((atype == sol::type::userdata));
sol::object u = lua.get<sol::object>("u");
sol::object v = lua.get<sol::object>("v");
REQUIRE((u.as<int>() == 3));
REQUIRE((v.as<int>() == 5));
sol::object b = lua.get<sol::object>("b");
auto btype = b.get_type();
REQUIRE((btype == sol::type::userdata));
sol::object w = lua.get<sol::object>("w");
sol::object x = lua.get<sol::object>("x");
REQUIRE((w.as<int>() == 1));
REQUIRE((x.as<int>() == 3));
sol::object c = lua.get<sol::object>("c");
auto ctype = c.get_type();
REQUIRE((ctype == sol::type::userdata));
sol::object y = lua.get<sol::object>("y");
sol::object z = lua.get<sol::object>("z");
REQUIRE((y.as<int>() == 7));
REQUIRE((z.as<int>() == 9));
}