浏览代码

Check user types on the stack via a type identifier

Ole Krüger 10 年之前
父节点
当前提交
a7154c17d6
共有 1 个文件被更改,包括 92 次插入21 次删除
  1. 92 21
      lib/luwra/usertypes.hpp

+ 92 - 21
lib/luwra/usertypes.hpp

@@ -17,7 +17,75 @@
 LUWRA_NS_BEGIN
 
 namespace internal {
-	template <typename T, typename... A> inline
+	using UserTypeID = const void*;
+
+	/**
+	 * User type identifier
+	 */
+	template <typename T>
+	UserTypeID user_type_id = (void*) INTPTR_MAX;
+
+	/**
+	 * Registry name for a meta table which is associated with a user type
+	 */
+	template <typename T>
+	std::string user_type_reg_name =
+		"UD#" + std::to_string(uintptr_t(&user_type_id<T>));
+
+	/**
+	 * Register a new meta table for a user type T.
+	 */
+	template <typename T> static inline
+	void new_user_type_id(State* state) {
+		luaL_newmetatable(state, user_type_reg_name<T>.c_str());
+
+		// Use the address as user type identifier
+		user_type_id<T> = lua_topointer(state, -1);
+	}
+
+	/**
+	 * Get the identifier for a user type at the given index.
+	 */
+	static inline
+	UserTypeID get_user_type_id(State* state, int index) {
+		if (!lua_isuserdata(state, index))
+			return nullptr;
+
+		if (lua_getmetatable(state, index)) {
+			UserTypeID type_id = lua_topointer(state, -1);
+			lua_pop(state, 1);
+			return type_id;
+		} else {
+			return nullptr;
+		}
+	}
+
+	/**
+	 * Check if the value at the given index if a user type T.
+	 */
+	template <typename T> static inline
+	T* check_user_type(State* state, int index) {
+		UserTypeID uid = get_user_type_id(state, index);
+		if (uid == user_type_id<T>) {
+			return static_cast<T*>(lua_touserdata(state, index));
+		} else {
+			std::string error_msg =
+				"Expected user type " + std::to_string(uintptr_t(user_type_id<T>));
+			luaL_argerror(state, index, error_msg.c_str());
+			return nullptr;
+		}
+	}
+
+	template <typename T> static inline
+	void apply_user_type_meta_table(State* state) {
+		luaL_getmetatable(state, user_type_reg_name<T>.c_str());
+		lua_setmetatable(state, -2);
+	}
+
+	/**
+	 * Lua C function to construct a user type T with parameters A
+	 */
+	template <typename T, typename... A> static inline
 	int construct_user_type(State* state) {
 		return internal::Layout<int(A...)>::direct(
 			state,
@@ -27,7 +95,10 @@ namespace internal {
 		);
 	}
 
-	template <typename T> inline
+	/**
+	 * Lua C function to destruct a user type T
+	 */
+	template <typename T> static inline
 	int destruct_user_type(State* state) {
 		if (!lua_islightuserdata(state, 1))
 			Value<T&>::read(state, 1).~T();
@@ -35,19 +106,21 @@ namespace internal {
 		return 0;
 	}
 
-	template <typename T>
-	std::string user_type_identifier =
-		"UD#" + std::to_string(uintmax_t(&destruct_user_type<T>));
-
-	template <typename T>
+	/**
+	 * Create a string representation for user type T.
+	 */
+	template <typename T> static
 	std::string stringify_user_type(T& val) {
 		return
-			internal::user_type_identifier<T>
+			internal::user_type_reg_name<T>
 			+ "@"
-			+ std::to_string(uintmax_t(&val));
+			+ std::to_string(uintptr_t(&val));
 	}
 
-	template <typename T, typename R, R T::* property_pointer> inline
+	/**
+	 * Lua C function for a property accessor.
+	 */
+	template <typename T, typename R, R T::* property_pointer> static inline
 	int access_user_type_property(State* state) {
 		if (lua_gettop(state) > 1) {
 			// Setter
@@ -72,6 +145,9 @@ namespace internal {
 		using MethodPointerType = R (T::*)(A...);
 		using FunctionSignature = R (T&, A...);
 
+		/**
+		 * This function is a wrapped around the invocation of a given method.
+		 */
 		template <MethodPointerType method_pointer> static inline
 		R call(T& parent, A... args) {
 			return (parent.*method_pointer)(std::forward<A>(args)...);
@@ -89,9 +165,7 @@ template <typename T>
 struct Value<T&> {
 	static inline
 	T& read(State* state, int n) {
-		return *static_cast<T*>(
-			luaL_checkudata(state, n, internal::user_type_identifier<T>.c_str())
-		);
+		return *internal::check_user_type<T>(state, n);
 	}
 
 	template <typename... A> static inline
@@ -107,8 +181,7 @@ struct Value<T&> {
 		new (mem) T(std::forward<A>(args)...);
 
 		// Set metatable for this type
-		luaL_getmetatable(state, internal::user_type_identifier<T>.c_str());
-		lua_setmetatable(state, -2);
+		internal::apply_user_type_meta_table<T>(state);
 
 		return 1;
 	}
@@ -123,9 +196,7 @@ template <typename T>
 struct Value<T*> {
 	static inline
 	T* read(State* state, int n) {
-		return static_cast<T*>(
-			luaL_checkudata(state, n, internal::user_type_identifier<T>.c_str())
-		);
+		return internal::check_user_type<T>(state, n);
 	}
 
 	static inline
@@ -134,8 +205,7 @@ struct Value<T*> {
 		lua_pushlightuserdata(state, instance);
 
 		// Set metatable for this type
-		luaL_getmetatable(state, internal::user_type_identifier<T>.c_str());
-		lua_setmetatable(state, -2);
+		internal::apply_user_type_meta_table<T>(state);
 
 		return 1;
 	}
@@ -207,7 +277,8 @@ void register_user_type(
 	const std::map<const char*, CFunction>& meta_methods = {}
 ) {
 	// Setup an appropriate meta table name
-	luaL_newmetatable(state, internal::user_type_identifier<T>.c_str());
+	// luaL_newmetatable(state, internal::user_type_reg_name<T>.c_str());
+	internal::new_user_type_id<T>(state);
 
 	// Register methods
 	if (methods.size() > 0 && meta_methods.count("__index") == 0) {