浏览代码

Make Lua functions callable from C++ using NativeFunction

Ole 10 年之前
父节点
当前提交
6cdd1d49b5
共有 3 个文件被更改,包括 87 次插入3 次删除
  1. 59 2
      lib/luwra/functions.hpp
  2. 8 0
      lib/luwra/types.hpp
  3. 20 1
      tests/functions.cpp

+ 59 - 2
lib/luwra/functions.hpp

@@ -23,10 +23,10 @@ namespace internal {
 	};
 
 	template <typename R, typename... A>
-	struct FunctionWrapper<R (A...)> {
+	struct FunctionWrapper<R(A...)> {
 		template <R (* fun)(A...)> static inline
 		int invoke(State* state) {
-			return map<R (A...)>(state, fun);
+			return map<R(A...)>(state, fun);
 		}
 	};
 
@@ -35,6 +35,63 @@ namespace internal {
 	struct FunctionWrapper<R(*)(A...)>: FunctionWrapper<R(A...)> {};
 }
 
+template <typename S>
+struct NativeFunction {
+	static_assert(
+		sizeof(S) == -1,
+		"Parameter to NativeFunction is not a valid signature"
+	);
+};
+
+/**
+ * A callable native Lua function.
+ * \note This value is only as long as it exists on the stack.
+ */
+template <typename R, typename... A>
+struct NativeFunction<R(A...)> {
+	State* state;
+	int index;
+
+	inline
+	R operator ()(A&&... args) {
+		lua_pushvalue(state, index);
+		size_t numArgs = push(state, std::forward<A>(args)...);
+
+		lua_call(state, numArgs, 1);
+		R returnValue = read<R>(state, -1);
+
+		lua_pop(state, 1);
+		return returnValue;
+	}
+};
+
+/**
+ * A callable native Lua function.
+ * \note This value is only as long as it exists on the stack.
+ */
+template <typename... A>
+struct NativeFunction<void(A...)> {
+	State* state;
+	int index;
+
+	inline
+	void operator ()(A&&... args) {
+		lua_pushvalue(state, index);
+		size_t numArgs = push(state, std::forward<A>(args)...);
+
+		lua_call(state, numArgs, 0);
+	}
+};
+
+template <typename R, typename... A>
+struct Value<NativeFunction<R(A...)>> {
+	static inline
+	NativeFunction<R(A...)> read(State* state, int index) {
+		luaL_checktype(state, index, LUA_TFUNCTION);
+		return {state, index};
+	}
+};
+
 LUWRA_NS_END
 
 /**

+ 8 - 0
lib/luwra/types.hpp

@@ -71,6 +71,14 @@ struct Value<std::nullptr_t> {
 	}
 };
 
+/**
+ * This does nothing.
+ */
+static inline
+size_t push(State*) {
+	return 0;
+}
+
 /**
  * Convenient wrapped for [Value<T>::push](@ref Value<T>::push).
  */

+ 20 - 1
tests/functions.cpp

@@ -109,4 +109,23 @@ TEST_CASE("wrap_function") {
 	lua_close(state);
 }
 
-// TODO: Test whether type-checking works properly
+TEST_CASE("NativeFunction") {
+	luwra::StateWrapper state;
+
+	SECTION("with return value") {
+		REQUIRE(luaL_dostring(state, "return function (x, y) return x + y end") == LUA_OK);
+
+		auto fun = luwra::read<luwra::NativeFunction<int(int, int)>>(state, -1);
+		REQUIRE(fun(13, 37) == 50);
+	}
+
+	SECTION("without return value") {
+		REQUIRE(luaL_dostring(state, "return function (x, y) returnValue = x + y end") == LUA_OK);
+
+		auto fun = luwra::read<luwra::NativeFunction<void(int, int)>>(state, -1);
+		fun(13, 37);
+
+		int returnValue = state["returnValue"];
+		REQUIRE(returnValue == 50);
+	}
+}