Explorar o código

Replace Arbitrary with Reference

The new Reference type allows the user to handle values that do not
exist on the stack.
Ole %!s(int64=9) %!d(string=hai) anos
pai
achega
8374bbe784
Modificáronse 2 ficheiros con 111 adicións e 36 borrados
  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);
 	}
 };