Skip to content

Commit

Permalink
Merge pull request #125 from AnonymousProgrammerLandau/TypesafetyForC…
Browse files Browse the repository at this point in the history
…lasses

Add more typesafety for registered classes
  • Loading branch information
jeremyong committed Nov 25, 2015
2 parents da094ec + e4512a2 commit 0b71f9d
Show file tree
Hide file tree
Showing 19 changed files with 594 additions and 164 deletions.
10 changes: 9 additions & 1 deletion include/selene/BaseFun.h
Expand Up @@ -2,7 +2,7 @@

#include "exotics.h"
#include <exception>
#include "exception.h"
#include "ExceptionHandler.h"
#include <functional>
#include "primitives.h"
#include <tuple>
Expand All @@ -19,12 +19,17 @@ namespace detail {
inline int _lua_dispatcher(lua_State *l) {
BaseFun *fun = (BaseFun *)lua_touserdata(l, lua_upvalueindex(1));
_lua_check_get raiseParameterConversionError = nullptr;
const char * wrong_meta_table = nullptr;
int erroneousParameterIndex = 0;
try {
return fun->Apply(l);
} catch (GetParameterFromLuaTypeError & e) {
raiseParameterConversionError = e.checked_get;
erroneousParameterIndex = e.index;
} catch (GetUserdataParameterFromLuaTypeError & e) {
wrong_meta_table = lua_pushlstring(
l, e.metatable_name.c_str(), e.metatable_name.length());
erroneousParameterIndex = e.index;
} catch (std::exception & e) {
lua_pushstring(l, e.what());
Traceback(l);
Expand All @@ -38,6 +43,9 @@ inline int _lua_dispatcher(lua_State *l) {
if(raiseParameterConversionError) {
raiseParameterConversionError(l, erroneousParameterIndex);
}
else if(wrong_meta_table) {
luaL_checkudata(l, erroneousParameterIndex, wrong_meta_table);
}

return lua_error(l);
}
Expand Down
38 changes: 6 additions & 32 deletions include/selene/Class.h
Expand Up @@ -22,14 +22,12 @@ template <typename T,
typename... Members>
class Class : public BaseClass {
private:
bool _should_erase = true;
std::string _name;
std::string _metatable_name;
std::unique_ptr<A> _ctor;
std::unique_ptr<Dtor<T>> _dtor;
using Funs = std::vector<std::unique_ptr<BaseFun>>;
Funs _funs;
MetatableRegistry& _meta_registry;

void _register_ctor(lua_State *state) {
_ctor.reset(new A(state, _metatable_name.c_str()));
Expand Down Expand Up @@ -87,7 +85,7 @@ class Class : public BaseClass {
void _register_member(lua_State *state,
const char *fun_name,
Ret(T::*fun)(Args&&...)) {
std::function<Ret(T*, Args&&...)> lambda = [fun](T *t, Args&&... args) {
std::function<Ret(T*, Args&&...)> lambda = [fun](T *t, Args&&... args) -> Ret {
return (t->*fun)(std::forward<Args>(args)...);
};
constexpr int arity = detail::_arity<Ret>::value;
Expand Down Expand Up @@ -139,44 +137,20 @@ class Class : public BaseClass {

public:
Class(lua_State *state,
MetatableRegistry &meta_registry,
const std::string &name,
Members... members) : _name(name), _meta_registry(meta_registry) {
Members... members) : _name(name) {
_metatable_name = _name + "_lib";
_meta_registry.Insert(typeid(T), _metatable_name);
luaL_newmetatable(state, _metatable_name.c_str());
MetatableRegistry::PushNewMetatable(state, typeid(T), _metatable_name);
_register_dtor(state);
_register_ctor(state);
_register_members(state, members...);
lua_pushvalue(state, -1);
lua_setfield(state, -1, "__index");
}
~Class() {
if (_should_erase) _meta_registry.Erase(typeid(T));
}
~Class() = default;
Class(const Class &) = delete;
Class& operator=(const Class &) = delete;
Class(Class &&other)
: _should_erase{true}
, _name{std::move(other._name)}
, _metatable_name{std::move(other._metatable_name)}
, _ctor{std::move(other._ctor)}
, _dtor{std::move(other._dtor)}
, _funs{std::move(other._funs)}
, _meta_registry{other._meta_registry} {
other._should_erase = false;
}
Class& operator=(Class &&other) {
if (&other == this) return *this;
_name = std::move(other._name);
_metatable_name = std::move(other._metatable_name);
_ctor = std::move(other._ctor);
_dtor = std::move(other._dtor);
_funs = std::move(other._funs);
_meta_registry = other._meta_registry;
other._should_erase = false;
_should_erase = true;
return *this;
}
Class(Class &&other) = default;
Class& operator=(Class &&other) = default;
};
}
12 changes: 6 additions & 6 deletions include/selene/Ctor.h
Expand Up @@ -12,12 +12,12 @@ class Ctor : public BaseFun {

public:
Ctor(lua_State *l,
const std::string &metatable_name) {
_ctor = [metatable_name](lua_State *state, Args... args) {
void *addr = lua_newuserdata(state, sizeof(T));
new(addr) T(args...);
luaL_setmetatable(state, metatable_name.c_str());
};
const std::string &metatable_name)
: _ctor([metatable_name](lua_State *state, Args... args) {
void *addr = lua_newuserdata(state, sizeof(T));
new(addr) T(args...);
luaL_setmetatable(state, metatable_name.c_str());
}) {
lua_pushlightuserdata(l, (void *)static_cast<BaseFun *>(this));
lua_pushcclosure(l, &detail::_lua_dispatcher, 1);
lua_setfield(l, -2, "new");
Expand Down
Expand Up @@ -47,7 +47,7 @@ inline void store_current_exception(lua_State * l, char const * what) {

luaL_getmetatable(l, _stored_exception_metatable_name()->c_str());
if(lua_isnil(l, -1)) {
lua_settop(l, -2);
lua_pop(l, 1);
_register_stored_exception_metatable(l);
}

Expand Down
24 changes: 24 additions & 0 deletions include/selene/ExceptionTypes.h
@@ -0,0 +1,24 @@
#pragma once

#include <exception>
#include <utility>

namespace sel {

class SeleneException : public std::exception {};

class TypeError : public SeleneException {
std::string _message;
public:
explicit TypeError(std::string expected)
: _message(std::move(expected)
+ " expected, got no object.") {}
explicit TypeError(std::string expected, std::string const & actual)
: _message(std::move(expected)
+ " expected, got " + actual + '.') {}
char const * what() const noexcept override {
return _message.c_str();
}
};

}
8 changes: 2 additions & 6 deletions include/selene/Fun.h
@@ -1,7 +1,6 @@
#pragma once

#include "BaseFun.h"
#include "MetatableRegistry.h"
#include "primitives.h"
#include <string>

Expand All @@ -11,12 +10,10 @@ class Fun : public BaseFun {
private:
using _fun_type = std::function<Ret(detail::decay_primitive<Args>...)>;
_fun_type _fun;
MetatableRegistry &_meta_registry;

public:
Fun(lua_State *&l,
MetatableRegistry &meta_registry,
_fun_type fun) : _fun(fun), _meta_registry(meta_registry) {
_fun_type fun) : _fun(fun) {
lua_pushlightuserdata(l, (void *)static_cast<BaseFun *>(this));
lua_pushcclosure(l, &detail::_lua_dispatcher, 1);
}
Expand All @@ -27,7 +24,7 @@ class Fun : public BaseFun {
std::tuple<detail::decay_primitive<Args>...> args =
detail::_get_args<detail::decay_primitive<Args>...>(l);
Ret value = detail::_lift(_fun, args);
detail::_push(l, _meta_registry, std::forward<Ret>(value));
detail::_push(l, std::forward<Ret>(value));
return N;
}

Expand All @@ -41,7 +38,6 @@ class Fun<0, void, Args...> : public BaseFun {

public:
Fun(lua_State *&l,
MetatableRegistry &,
_fun_type fun) : _fun(fun) {
lua_pushlightuserdata(l, (void *)static_cast<BaseFun *>(this));
lua_pushcclosure(l, &detail::_lua_dispatcher, 1);
Expand Down
168 changes: 144 additions & 24 deletions include/selene/MetatableRegistry.h
Expand Up @@ -4,37 +4,157 @@
#include <typeinfo>
#include <unordered_map>

extern "C" {
#include <lua.h>
#include <lauxlib.h>
}

namespace sel {
class MetatableRegistry {
private:
using TypeID = std::reference_wrapper<const std::type_info>;
struct Hasher {
std::size_t operator()(TypeID code) const {
return code.get().hash_code();
}
};
struct EqualTo {
bool operator()(TypeID lhs, TypeID rhs) const {
return lhs.get() == rhs.get();

namespace detail {
struct GetUserdataParameterFromLuaTypeError {
std::string metatable_name;
int index;
};
}

namespace MetatableRegistry {
using TypeID = std::reference_wrapper<const std::type_info>;
namespace detail {

static inline void _create_table_in_registry(lua_State *state, const std::string & name) {
lua_pushlstring(state, name.c_str(), name.size());
lua_newtable(state);
lua_settable(state, LUA_REGISTRYINDEX);
}

static inline void _push_names_table(lua_State *state) {
lua_pushliteral(state, "selene_metatable_names");
lua_gettable(state, LUA_REGISTRYINDEX);
}

static inline void _push_meta_table(lua_State *state) {
lua_pushliteral(state, "selene_metatables");
lua_gettable(state, LUA_REGISTRYINDEX);
}

static inline void _push_typeinfo(lua_State *state, TypeID type) {
lua_pushlightuserdata(state, const_cast<std::type_info*>(&type.get()));
}

static inline void _get_metatable(lua_State *state, TypeID type) {
detail::_push_meta_table(state);
detail::_push_typeinfo(state, type);
lua_gettable(state, -2);
lua_remove(state, -2);
}

}

static inline void Create(lua_State *state) {
detail::_create_table_in_registry(state, "selene_metatable_names");
detail::_create_table_in_registry(state, "selene_metatables");
}

static inline void PushNewMetatable(lua_State *state, TypeID type, const std::string& name) {
detail::_push_names_table(state);

detail::_push_typeinfo(state, type);
lua_pushlstring(state, name.c_str(), name.size());
lua_settable(state, -3);

lua_pop(state, 1);


luaL_newmetatable(state, name.c_str()); // Actual result.


detail::_push_meta_table(state);

detail::_push_typeinfo(state, type);
lua_pushvalue(state, -3);
lua_settable(state, -3);

lua_pop(state, 1);
}

static inline bool SetMetatable(lua_State *state, TypeID type) {
detail::_get_metatable(state, type);

if(lua_istable(state, -1)) {
lua_setmetatable(state, -2);
return true;
}

lua_pop(state, 1);
return false;
}

static inline std::string GetTypeName(lua_State *state, TypeID type) {
std::string name("unregistered type");

detail::_push_names_table(state);
detail::_push_typeinfo(state, type);
lua_gettable(state, -2);

if(lua_isstring(state, -1)) {
size_t len = 0;
char const * str = lua_tolstring(state, -1, &len);
name.assign(str, len);
}

lua_pop(state, 2);
return name;
}

static inline std::string GetTypeName(lua_State *state, int index) {
std::string name;

if(lua_getmetatable(state, index)) {
lua_pushliteral(state, "__name");
lua_gettable(state, -2);

if(lua_isstring(state, -1)) {
size_t len = 0;
char const * str = lua_tolstring(state, -1, &len);
name.assign(str, len);
}
};
std::unordered_map<TypeID, const std::string*, Hasher, EqualTo> _metatables;

public:
MetatableRegistry() {}
lua_pop(state, 2);
}

inline void Insert(TypeID type, const std::string& name) {
_metatables[type] = &name;
if(name.empty()) {
name = lua_typename(state, lua_type(state, index));
}

inline void Erase(TypeID type) {
_metatables.erase(type);
return name;
}

static inline bool IsType(lua_State *state, TypeID type, const int index) {
bool equal = true;

if(lua_getmetatable(state, index)) {
detail::_get_metatable(state, type);
equal = lua_istable(state, -1) && lua_rawequal(state, -1, -2);
lua_pop(state, 2);
} else {
detail::_get_metatable(state, type);
equal = !lua_istable(state, -1);
lua_pop(state, 1);
}

inline const std::string* Find(TypeID type) {
auto it = _metatables.find(type);
if (it == _metatables.end()) return nullptr;
return it->second;
return equal;
}

static inline void CheckType(lua_State *state, TypeID type, const int index) {
if(!IsType(state, type, index)) {
throw sel::detail::GetUserdataParameterFromLuaTypeError{
GetTypeName(state, type),
index
};
}
};
}

}

}

0 comments on commit 0b71f9d

Please sign in to comment.