Skip to content

Instantly share code, notes, and snippets.

@Rapptz
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rapptz/d1ea10126c53cd6e7a01 to your computer and use it in GitHub Desktop.
Save Rapptz/d1ea10126c53cd6e7a01 to your computer and use it in GitHub Desktop.
#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