-
-
Save Rapptz/d1ea10126c53cd6e7a01 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sol.hpp> | |
#include <vector> | |
// naive implementation | |
namespace sol { | |
template<typename T> | |
inline void get_arg(lua_State* L, int narg, T& t) { | |
auto expected = type_of<T>(); | |
type_assert(L, narg, expected); | |
t = stack::get<T>(L, narg); | |
} | |
template<typename Tuple, size_t... Indices> | |
inline void get_args_impl(lua_State* L, Tuple& args, indices<Indices...>) { | |
using swallow = int[]; | |
void(swallow{(get_arg(L, Indices, std::get<Indices>(args)), 0)...}); | |
} | |
template<typename... Args> | |
inline std::tuple<Args...> get_args(lua_State* L) { | |
std::tuple<Args...> args; | |
get_args_impl(L, args, build_indices<sizeof...(Args)>{}); | |
return args; | |
} | |
template<typename Class, typename Tuple, size_t... Indices> | |
inline Class* unpack_impl(const Tuple& t, indices<Indices...>) { | |
return new Class(std::get<Indices>(t)...); | |
} | |
template<typename Class, typename... Args> | |
inline Class* unpack(const std::tuple<Args...>& t) { | |
return unpack_impl<Class>(t, build_indices<sizeof...(Args)>{}); | |
} | |
template<typename...> | |
struct arity; | |
template<typename T> | |
struct arity<T> : public std::integral_constant<size_t, 1> {}; | |
template<typename... Args> | |
struct arity<std::tuple<Args...>> : public std::integral_constant<size_t, sizeof...(Args)> {}; | |
template<typename Class> | |
static Class* is_class(lua_State* L, const std::string& metatable) { | |
return *(Class**)luaL_checkudata(L, lua_upvalueindex(1), metatable.c_str()); | |
} | |
struct base_function { | |
virtual ~base_function() = default; | |
virtual int call(lua_State*) = 0; | |
}; | |
template<typename Class, typename Return, typename... Args> | |
struct member_function : public base_function { | |
public: | |
using function_type = Return(Class::*)(Args...); | |
private: | |
function_type func; | |
std::string metatable; | |
template<size_t... Indices> | |
Return apply_impl(Class* c, const std::tuple<Args...>& args, indices<Indices...>) { | |
return (c->*func)(std::get<Indices>(args)...); | |
} | |
Return apply(Class* c, const std::tuple<Args...>& args) { | |
return apply_impl(c, args, build_indices<sizeof...(Args)>{}); | |
} | |
public: | |
member_function(function_type func, std::string metatable): func(func), metatable(std::move(metatable)) {} | |
int call_impl(lua_State* L, std::false_type) { | |
const auto n = lua_gettop(L); | |
if(n != sizeof...(Args) + 1) { | |
return luaL_error(L, "Received %d arguments, expected %d", n, sizeof...(Args) + 1); | |
} | |
auto* c = is_class<Class>(L, metatable); | |
auto args = get_args<Args...>(L); | |
Return r = apply(c, args); | |
stack::push(L, r); | |
return arity<Decay<Return>>::value; | |
} | |
int call_impl(lua_State* L, std::true_type) { | |
const auto n = lua_gettop(L); | |
if(n != sizeof...(Args) + 1) { | |
return luaL_error(L, "Received %d arguments, expected %d", n, sizeof...(Args) + 1); | |
} | |
auto* c = is_class<Class>(L, metatable); | |
auto args = get_args<Args...>(L); | |
apply(c, args); | |
return 0; | |
} | |
int call(lua_State* L) { | |
return call_impl(L, std::is_void<Return>{}); | |
} | |
}; | |
template<typename Class, typename... Args> | |
struct constructor : public base_function { | |
private: | |
std::string metatable; | |
public: | |
constructor(std::string metatable): metatable(std::move(metatable)) {} | |
int call(lua_State* L) { | |
const auto n = lua_gettop(L); | |
if(n != sizeof...(Args)) { | |
return luaL_error(L, "Received %d arguments, expected %d", n, sizeof...(Args)); | |
} | |
auto args = get_args<Args...>(L); | |
Class** data = (Class**)lua_newuserdata(L, sizeof(Class*)); | |
*data = unpack<Class>(args); | |
luaL_getmetatable(L, metatable.c_str()); | |
lua_setmetatable(L, -2); | |
return 1; | |
} | |
}; | |
template<typename Class> | |
struct destructor : public base_function { | |
private: | |
std::string metatable; | |
public: | |
destructor(std::string metatable): metatable(std::move(metatable)) {} | |
int call(lua_State* L) { | |
Class* c = is_class<Class>(L, metatable); | |
delete c; | |
return 0; | |
} | |
}; | |
int dispatch(lua_State* L) { | |
base_function* func = static_cast<base_function*>(lua_touserdata(L, lua_upvalueindex(1))); | |
return func->call(L); | |
} | |
struct registry { | |
const char* name; | |
std::unique_ptr<base_function> function; | |
registry(const char* name, std::unique_ptr<base_function> f): name(name), function(std::move(f)) {} | |
void commit(lua_State* L) const { | |
lua_pushlightuserdata(L, static_cast<void*>(function.get())); | |
lua_pushcclosure(L, dispatch, 1); | |
lua_setfield(L, -2, name); | |
} | |
}; | |
template<typename Class, typename... Args> | |
struct userdata { | |
private: | |
std::vector<registry> functions; | |
lua_State* L; | |
std::string name; | |
std::string metatable; | |
template<typename T, typename... Ts> | |
std::unique_ptr<base_function> abstract_unique(Ts&&... ts) const { | |
return std::unique_ptr<base_function>{ new T(std::forward<Ts>(ts)...) }; | |
} | |
public: | |
userdata(lua_State* L, std::string name): L(L), name(std::move(name)) { | |
metatable = "sol.binding.class." + this->name; | |
functions.emplace_back("new", abstract_unique<constructor<Class, Args...>>(metatable)); | |
functions.emplace_back("__gc", abstract_unique<destructor<Class>>(metatable)); | |
} | |
template<typename Return, typename... FArgs> | |
userdata& member(const std::string& str, Return(Class::*func)(FArgs...)) { | |
functions.emplace_back(str.c_str(), abstract_unique<member_function<Class, Return, FArgs...>>(func, metatable)); | |
return *this; | |
} | |
void commit() { | |
luaL_newmetatable(L, metatable.c_str()); | |
for(auto&& func : functions) { | |
func.commit(L); | |
} | |
lua_pushvalue(L, -1); | |
lua_setfield(L, -1, "__index"); | |
// ? | |
lua_setglobal(L, name.c_str()); | |
} | |
}; | |
template<typename Class, typename... Args> | |
userdata<Class, Args...> create_class(state& s, const std::string& str) { | |
return { s.lua_state(), str }; | |
} | |
} // sol | |
#include <iostream> | |
struct f { | |
int x; | |
f(int x): x(x) {} | |
int add(int y) { | |
return x + y; | |
} | |
}; | |
int main() { | |
sol::state s; | |
sol::create_class<f, int>(s, "f").member("add", &f::add).commit(); | |
s.script("test = f.new(10)"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment