Bläddra i källkod

Remove MetatableName field requirement

Ole Krüger 10 år sedan
förälder
incheckning
0817a9c3eb
3 ändrade filer med 42 tillägg och 28 borttagningar
  1. 5 6
      README.md
  2. 3 7
      examples/methods.cpp
  3. 34 15
      lib/luwra/userdata.hpp

+ 5 - 6
README.md

@@ -9,14 +9,13 @@ examples will have no use to you.
 ### Types
 A template `Value<T>` exists to capsulate push and check mechanisms for a type `T`. Default
 specializations are implemented for C/C++ numeric types, `bool`, `const char*`,
-`std::string`, `Arbitrary` and `U&` where `U` is any type with a static field
-`const char* MetatableName` and a meta table registered with that name.
+`std::string`, `Arbitrary` and `U&` where `U` is a user type.
 
 The `Arbitrary` struct symbolizes any value on the stack. Instances of `Arbitrary` can be seen as
 references to an index on an execution stack. Note, these kind of references are only valid as long
 as their referenced value exists at the given index on the given stack.
 
-The `Value<U&>` specialization is designated to the instantiation and reference of  a user data
+The `Value<U&>` specialization is designated to the instantiation and reference of a user data
 type `U`.
 
 Any template specialization of `Value` which is expected to work with Luwra must provide a
@@ -115,6 +114,6 @@ lua_CFunction cfunc = WrapMethod<MyClass, lua_Number(lua_Number, lua_Number), &M
 ```
 
 ### User types
-Luwra also provides means to implement Lua user data types.
-Check out this [example](https://github.com/vapourismo/luwra/blob/master/examples/methods.cpp) for
-more.
+Something is going be here soon.
+
+Have a look at [the example](https://github.com/vapourismo/luwra/blob/master/examples/methods.cpp).

+ 3 - 7
examples/methods.cpp

@@ -4,10 +4,6 @@
 #include <iostream>
 
 struct Point {
-	// Luwra needs the MetatableName field in order to add a meta table to the Lua registry
-	static constexpr
-	const char* MetatableName = "Point";
-
 	lua_Number x, y;
 
 	Point(lua_Number x, lua_Number y):
@@ -34,10 +30,10 @@ int main() {
 	lua_State* state = luaL_newstate();
 	luaL_openlibs(state);
 
-	// Register the metatable for our Point type.
+	// Register our user type.
 	// This function also registers a garbage-collector hook and a string representation function.
 	// Both can be overwritten using the third parameter, which lets you add custom meta methods.
-	luwra::register_type_metatable<Point>(
+	luwra::register_user_type<Point>(
 		state,
 		// Methods which shall be availabe in the Lua user data, need to be declared here
 		{
@@ -49,7 +45,7 @@ int main() {
 		}
 	);
 
-	// What's left, is register a constructor for our type.
+	// What's left, is registering a constructor for our type.
 	// We have to specify which parameters our constructor takes, because there might be more than
 	// one constructor to deal with.
 	auto wrapped_ctor = luwra::WrapConstructor<Point, lua_Number, lua_Number>;

+ 34 - 15
lib/luwra/userdata.hpp

@@ -13,28 +13,37 @@
 
 #include <sstream>
 #include <utility>
+#include <atomic>
+#include <cassert>
 
 LUWRA_NS_BEGIN
 
 /**
- * Instances of userdata shall always be used as references, because other Lua types can not be
- * converted to references, hence this allows the compiler to differentiate between them.
+ * Instances of user types shall always be used as references, because Lua values can not be
+ * referenced, hence this allows the compiler to differentiate between them.
  */
 template <typename T>
 struct Value<T&> {
+	static
+	std::string MetatableName;
+
 	static inline
 	T& read(State* state, int n) {
+		assert(MetatableName.size() > 0);
+
 		return *static_cast<T*>(
-			luaL_checkudata(state, n, T::MetatableName)
+			luaL_checkudata(state, n, MetatableName.c_str())
 		);
 	}
 
 	template <typename... A> static inline
 	int push(State* state, A&&... args) {
+		assert(MetatableName.size() > 0);
+
 		void* mem = lua_newuserdata(state, sizeof(T));
 
 		if (!mem) {
-			luaL_error(state, "Failed to allocate user data");
+			luaL_error(state, "Failed to allocate user type");
 			return -1;
 		}
 
@@ -42,32 +51,35 @@ struct Value<T&> {
 		new (mem) T(std::forward<A>(args)...);
 
 		// Set metatable for this type
-		luaL_getmetatable(state, T::MetatableName);
+		luaL_getmetatable(state, MetatableName.c_str());
 		lua_setmetatable(state, -2);
 
 		return 1;
 	}
 };
 
+template <typename T>
+std::string Value<T&>::MetatableName;
+
 namespace internal {
 	template <typename T, typename... A>
-	int userdata_ctor(State* state) {
+	int user_type_ctor(State* state) {
 		return apply(state, std::function<int(A...)>([state](A... args) {
 			return Value<T&>::push(state, args...);
 		}));
 	}
 
 	template <typename T>
-	int userdata_dtor(State* state) {
+	int user_type_dtor(State* state) {
 		Value<T&>::read(state, 1).~T();
 		return 0;
 	}
 
 	template <typename T>
-	int userdata_tostring(State* state) {
+	int user_type_tostring(State* state) {
 		return Value<std::string>::push(
 			state,
-			"TypeInstance" + std::string(T::MetatableName)
+			Value<T&>::MetatableName
 		);
 	}
 }
@@ -79,12 +91,19 @@ namespace internal {
  * needed.
  */
 template <typename T> static inline
-void register_type_metatable(
+void register_user_type(
 	State* state,
 	std::initializer_list<std::pair<const char*, CFunction>> methods,
 	std::initializer_list<std::pair<const char*, CFunction>> meta_methods = {}
 ) {
-	luaL_newmetatable(state, T::MetatableName);
+	static
+	std::atomic_size_t mt_counter;
+
+	// Setup an appropriate meta table name
+	if (Value<T&>::MetatableName.size() == 0)
+		Value<T&>::MetatableName = "UD#" + std::to_string(mt_counter++);
+
+	luaL_newmetatable(state, Value<T&>::MetatableName.c_str());
 
 	// Register methods
 	lua_pushstring(state, "__index");
@@ -100,12 +119,12 @@ void register_type_metatable(
 
 	// Register garbage-collection hook
 	lua_pushstring(state, "__gc");
-	lua_pushcfunction(state, &internal::userdata_dtor<T>);
+	lua_pushcfunction(state, &internal::user_type_dtor<T>);
 	lua_rawset(state, -3);
 
 	// Register string representation function
 	lua_pushstring(state, "__tostring");
-	lua_pushcfunction(state, &internal::userdata_tostring<T>);
+	lua_pushcfunction(state, &internal::user_type_tostring<T>);
 	lua_rawset(state, -3);
 
 	// Insert meta methods
@@ -120,7 +139,7 @@ void register_type_metatable(
 }
 
 static inline
-void register_userdata_metatable(
+void register_user_type_metatable(
 	State* state, const char* name,
 	std::initializer_list<std::pair<const char*, CFunction>> methods,
 	std::initializer_list<std::pair<const char*, CFunction>> meta_methods = {}
@@ -158,7 +177,7 @@ void register_userdata_metatable(
  * to use during construction.
  */
 template <typename T, typename... A>
-constexpr CFunction WrapConstructor = &internal::userdata_ctor<T, A...>;
+constexpr CFunction WrapConstructor = &internal::user_type_ctor<T, A...>;
 
 LUWRA_NS_END