#注册C函数给lua

lua中存在 typedef int (*lua_CFunction) (lua_State *L); C 函数的类型。

为了正确的和 Lua 通讯, C 函数必须使用下列协议。 这个协议定义了参数以及返回值传递方法: C 函数通过 Lua 中的栈来接受参数, 参数以正序入栈(第一个参数首先入栈)。 因此,当函数开始的时候, lua_gettop(L) 可以返回函数收到的参数个数。 第一个参数(如果有的话)在索引 1 的地方, 而最后一个参数在索引 lua_gettop(L) 处。 当需要向 Lua 返回值的时候, C 函数只需要把它们以正序压到堆栈上(第一个返回值最先压入), 然后返回这些返回值的个数。 在这些返回值之下的,堆栈上的东西都会被 Lua 丢掉。 和 Lua 函数一样,从 Lua 中调用 C 函数也可以有很多返回值

我们可以使用luaL_Reg 构建一个数组,将函数类型为lua_CFunction 的函数快速注册给lua。

typedef struct luaL_Reg {
  const char *name;
  lua_CFunction func;
} luaL_Reg;

void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);

用于 luaL_setfuncs 注册函数的数组类型。 name 指函数名,func 是函数指针。 任何 luaL_Reg 数组必须以一对 name 与 func 皆为 NULL 结束。

static int AddNumber(lua_State* pl)
{
	lua_pushnumber(L, 1+2);
	return 1;
}

static struct luaL_Reg arrayFunc[] =
{
	{"AddNumber", AddNumber},
	{NULL, NULL},
};

luaL_setfuncs(L, ObjFunc, 0);

但是这种方式虽然简单,但是不能传递参数。下面是一种可以注册复杂c函数的方式。使用了一些模板的相关功能。其中CAnyValue是一种简单的万能变量的实现方式,再以后的文章中我会详细介绍一下。

extern "C"
{
	#include "lua.h"
	#include "lauxlib.h"
	#include "lualib.h"
}
#include "CAnyValue.h"
#include "stdio.h"
#include "string.h"

#define INIT_VALUE(__type, __value) \
	__type __value; \
	InitValue(__value);

void InitValue(int& v)
{
	v = 0;
}

void InitValue(double& v)
{
	v = 0.00000f;
}

void InitValue(bool& v)
{
	v = true;
}

void InitValue(char*& v)
{
	v = NULL;
}

int GetLuaParam(lua_State* L, int nParamCnt, CAnyValue* v)
{
	for (int i = 0; i < nParamCnt; i++)
	{
		int index = i - nParamCnt;
		int n = lua_gettop(L);
		if ((index > n) || (index < -n) || (index == 0))
			return -1;

		switch (lua_type(L, index))
		{
		case LUA_TNIL:
			break;
		case LUA_TNUMBER:
			{
				int temp = (int)lua_tonumber(L, index);
				v->set(temp);
				break;
			}
		case LUA_TSTRING:
			{
				char* temp = (char*)lua_tostring(L, index);
				v->set(temp);
				break;
			}
		case LUA_TBOOLEAN:
			{
				bool temp = (bool)lua_toboolean(L, index);
				v->set(temp);
				break;
			}
		default:
			break;
		}
		v++;
	}
	return 1;
}


void Push(lua_State* L, int v)
{
	lua_pushnumber(L, v);
}

void Push(lua_State* L, bool v)
{
	lua_pushboolean(L, v);
}

void Push(lua_State* L, char* v)
{
	lua_pushstring(L, v);
}

template<typename T>
int Call(lua_State* L, T fn)
{
	return (T)(*fn)(L);
}

template<>
int Call(lua_State* L, int(*fn)(lua_State*))
{
	return (*fn)(L);
}

template<typename P1>
int Call(lua_State* L, int(*fn)(lua_State*, P1))
{
	CAnyValue anyValue[1];
	P1 arg1;
	InitValue(arg1);
	GetLuaParam(L, 1, &anyValue[0]);
	anyValue[0].get(arg1);

	return (*fn)(L, arg1);
}

template<typename P1, typename P2>
int Call(lua_State* L, int(*fn)(lua_State*, P1, P2))
{
	CAnyValue anyValue[2];
	P1 arg1;
	P2 arg2;
	InitValue(arg1);
	InitValue(arg2);
	GetLuaParam(L, 2, anyValue);
	anyValue[0].get(arg1);
	anyValue[1].get(arg2);

	return (*fn)(L, arg1, arg2);
}

template<typename P1, typename P2, typename P3>
int Call(lua_State* L, int(*fn)(lua_State*, P1, P2, P3))
{
	CAnyValue anyValue[3];
	P1 arg1;
	P2 arg2;
	P3 arg3;
	InitValue(arg1);
	InitValue(arg2);
	InitValue(arg3);
	GetLuaParam(L, 3, anyValue);
	anyValue[0].get(arg1);
	anyValue[1].get(arg2);
	anyValue[2].get(arg3);

	return (*fn)(L, arg1, arg2, arg3);
}

template<typename P1, typename P2, typename P3, typename P4>
int Call(lua_State* L, int(*fn)(lua_State*, P1, P2, P3, P4))
{
	CAnyValue anyValue[4];
	P1 arg1;
	P2 arg2;
	P3 arg3;
	P4 arg4;
	InitValue(arg1);
	InitValue(arg2);
	InitValue(arg3);
	InitValue(arg4);
	GetLuaParam(L, 4, anyValue);
	anyValue[0].get(arg1);
	anyValue[1].get(arg2);
	anyValue[2].get(arg3);
	anyValue[3].get(arg4);

	return (*fn)(L, arg1, arg2, arg3, arg4);
}

template<typename F>
int GlobalRegCFunc(lua_State* L)
{
	void** pFunc = (void**)lua_touserdata(L, lua_upvalueindex(1));
	return Call(L, (F)(*pFunc));
}


template<typename F>
void Register(lua_State* L, const char* funcName, F fn)
{
	unsigned char* pFunc = (unsigned char*)lua_newuserdata(L, sizeof(F));
	memcpy(pFunc, &fn, sizeof(F));
	lua_pushcclosure(L, GlobalRegCFunc<F>, 1);
	lua_setglobal(L, funcName);
}

int Add(lua_State* L, int a)
{
	int nRetCount = 0;
	lua_pushnumber(L, a+1);
	nRetCount++;
	return nRetCount;
}

int Plus(lua_State* L, int a, int b, char* connect)
{
	int nRetCount = 0;
	lua_pushnumber(L, a + b);
	nRetCount++;
	printf("%s\n", connect);
	return nRetCount;
}

int main()
{
	lua_State* L = luaL_newstate();
	if (!L)
		return 0;
	luaL_openlibs(L);
	
	
	Register(L, "Add", Add);
	Register(L, "Plus", Plus);

	int ret = luaL_loadfile(L, "test.lua");
	if (ret != 0)
	{
		printf("%s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
	}
	ret = lua_pcall(L, 0, 0, 0);
	if (ret != 0)
	{
		printf("%s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
	}
	return -1;
}