Преглед изворни кода

Replace Arbitrary with Reference

The new Reference type allows the user to handle values that do not
exist on the stack.
Ole пре 10 година
родитељ
комит
8374bbe784
2 измењених фајлова са 111 додато и 36 уклоњено
  1. 12 12
      lib/luwra/functions.hpp
  2. 99 24
      lib/luwra/types.hpp

+ 12 - 12
lib/luwra/functions.hpp

@@ -40,20 +40,20 @@ namespace internal {
  * \note This value is only available as long as it exists on the stack.
  */
 template <typename R>
-struct NativeFunction: Arbitrary {
+struct NativeFunction: Reference {
 	NativeFunction(State* state, int index):
-		Arbitrary(state, index)
+		Reference(state, index)
 	{}
 
 	template <typename... A> inline
 	R operator ()(A&&... args) {
-		push<Arbitrary>(state, *this);
-		size_t numArgs = push(state, std::forward<A>(args)...);
+		impl->push();
+		size_t numArgs = push(impl->state, std::forward<A>(args)...);
 
-		lua_call(state, numArgs, 1);
-		R returnValue = read<R>(state, -1);
+		lua_call(impl->state, numArgs, 1);
+		R returnValue = Value<R>::read(impl->state, -1);
 
-		lua_pop(state, 1);
+		lua_pop(impl->state, 1);
 		return returnValue;
 	}
 };
@@ -63,17 +63,17 @@ struct NativeFunction: Arbitrary {
  * \note This value is only available as long as it exists on the stack.
  */
 template <>
-struct NativeFunction<void>: Arbitrary {
+struct NativeFunction<void>: Reference {
 	NativeFunction(State* state, int index):
-		Arbitrary(state, index)
+		Reference(state, index)
 	{}
 
 	template <typename... A> inline
 	void operator ()(A&&... args) {
-		push<Arbitrary>(state, *this);
-		size_t numArgs = push(state, std::forward<A>(args)...);
+		impl->push();
+		size_t numArgs = push(impl->state, std::forward<A>(args)...);
 
-		lua_call(state, numArgs, 0);
+		lua_call(impl->state, numArgs, 0);
 	}
 };
 

+ 99 - 24
lib/luwra/types.hpp

@@ -242,47 +242,122 @@ struct Value<CFunction> {
 	}
 };
 
+namespace internal {
+	// Create reference the value pointed to by `index`. Does not remove the referenced value.
+	static inline
+	int referenceValue(State* state, int index) {
+		lua_pushvalue(state, index);
+		return luaL_ref(state, LUA_REGISTRYINDEX);
+	}
+
+	// Implementation of a reference which takes care of the lifetime of a Lua reference
+	struct ReferenceImpl {
+		State* const state;
+		int const ref;
+
+		// Reference a value at an index.
+		inline
+		ReferenceImpl(State* state, int index):
+			state(state),
+			ref(referenceValue(state, index))
+		{}
+
+		// Reference the value on top of stack.
+		inline
+		ReferenceImpl(State* state):
+			state(state),
+			ref(luaL_ref(state, LUA_REGISTRYINDEX))
+		{}
+
+		// A (smart) pointer to an instance may be copied and moved, but the instance itself must
+		// not be copied or moved. This allows us to have only one instance of `ReferenceImpl` per
+		// Lua reference.
+		ReferenceImpl(const ReferenceImpl& other) = delete;
+		ReferenceImpl(ReferenceImpl&& other) = delete;
+
+		inline
+		~ReferenceImpl() {
+			if (ref >= 0) luaL_unref(state, LUA_REGISTRYINDEX, ref);
+		}
+
+		// Small shortcut to make the `push`-implementations for `Table` and `Reference` consistent,
+		// since both use this struct internally.
+		inline
+		size_t push(State* targetState) {
+			lua_rawgeti(state, LUA_REGISTRYINDEX, ref);
+
+			if (state != targetState)
+				lua_xmove(state, targetState, 1);
+
+			return 1;
+		}
+
+		inline
+		size_t push() {
+			lua_rawgeti(state, LUA_REGISTRYINDEX, ref);
+			return 1;
+		}
+	};
+
+	using SharedReferenceImpl = std::shared_ptr<internal::ReferenceImpl>;
+}
+
 /**
- * An arbitrary value on an execution stack.
- * Note: this value is only available as long as it exists on its originating stack.
+ * Reference to an arbitrary value.
  */
-struct Arbitrary {
+struct Reference {
+	const internal::SharedReferenceImpl impl;
+
 	/**
-	 * Originating Lua state
+	 * Create a reference to the value at the given index.
 	 */
-	State* state;
+	inline
+	Reference(State* state, int index):
+		impl(std::make_shared<internal::ReferenceImpl>(state, index))
+	{}
 
 	/**
-	 * Stack index
+	 * Create a reference to the value at the top of the stack.
 	 */
-	int index;
-
-	Arbitrary(State* state, int index):
-		state(state), index(index < 0 ? lua_gettop(state) + (index + 1) : index)
+	inline
+	Reference(State* state):
+		impl(std::make_shared<internal::ReferenceImpl>(state))
 	{}
+
+	/**
+	 * Read the referenced value.
+	 */
+	template <typename T> inline
+	T read() {
+		size_t pushed = impl->push();
+		T ret = Value<T>::read(impl->state, -1);
+
+		lua_pop(impl->state, pushed);
+		return ret;
+	}
+
+	/**
+	 * Shortcut for `read<T>()`.
+	 */
+	template <typename T> inline
+	operator T() {
+		return read<T>();
+	}
 };
 
 /**
- * See [Arbitrary](@ref Arbitrary).
+ * See [Reference](@ref Reference).
  */
 template <>
-struct Value<Arbitrary> {
+struct Value<Reference> {
 	static inline
-	Arbitrary read(State* state, int index) {
-		if (index < 0)
-			index = lua_gettop(state) + (index + 1);
-
-		return Arbitrary {state, index};
+	Reference read(State* state, int index) {
+		return {state, index};
 	}
 
 	static inline
-	size_t push(State* state, const Arbitrary& value) {
-		lua_pushvalue(value.state, value.index);
-
-		if (value.state != state)
-			lua_xmove(value.state, state, 1);
-
-		return 1;
+	size_t push(State* state, const Reference& value) {
+		return value.impl->push(state);
 	}
 };