有时候我们需要通过C++向Lua传递包含多种类型数据的参数,但这些参数又没办法提前预知,也就是说,应用可能会有不同类型的参数,这些参数需要统一传入Lua进行处理。很显然,如果每次都要去判断每一个参数的类型,并调用不同的传递函数lua_push***,在代码上就显得拖沓冗余,并且效率低下。那么有没有办法定义一个统一的接口,能够接收常用的数据类型,并统一传给Lua呢?

可以利用类的多态性实现。

假如我们传递的数据类型包括int、double、bool、string,那么,让我们来定义一个基类和若干子类。

基类:

/* A base class for arguments from C/C++ to lua */
class TArg
{
public:
	TArg(){}
	virtual void push_value(lua_State* L) const = 0;
	virtual void set_value(int v){}     //set_value()函数用于更新数据时使用    
	virtual void set_value(double v){}
	virtual void set_value(bool v){}
	virtual void set_value(std::string v){}
};

int子类:

/* The derived class for the int type which will be pushed from C++ to Lua stack */
class TArgInt : public TArg
{
public:
	explicit TArgInt(int v) : _numv(v){}
	virtual void push_value(lua_State* L) const
	{
		lua_pushinteger(L, _numv);
	}

	virtual void set_value(int v)
	{
		_numv = v;
	}
	
private:
	int _numv;
};

double子类:

/* The derived class for the double type which will be pushed from C++ to Lua stack */
class TArgDouble : public TArg
{
public:
	explicit TArgDouble(double v) : _numv(v){}
	
	virtual void push_value(lua_State* L) const
	{
		lua_pushnumber(L, _numv);
	}

	virtual void set_value(double v)
	{
		_numv = v;
	}
	
private:
	double _numv;
};

string子类:

/* The derived class for the string type which will be pushed from C++ to Lua stack */
class TArgString : public TArg
{
public:
	explicit TArgString(std::string v) : _strv(v){}
	virtual void push_value(lua_State* L) const
	{
		lua_pushstring(L, _strv.c_str());
	}

	virtual void set_value(std::string v)
	{
		_strv = v;
	}
	
private:
	std::string _strv;
};

bool子类:

/* The derived class for the bool type which will be pushed from C++ to Lua stack */
class TArgBool : public TArg
{
public:
	explicit TArgBool(bool v) : _boolv(v){}
	virtual void push_value(lua_State* L) const
	{
		lua_pushboolean(L, _boolv);
	}

	virtual void set_value(bool v)
	{
		_boolv = v;
	}
	
private:
	bool _boolv;
};

好了,定义好了我们可能需要的类型对应的各个class,那么怎么把它们统一起来呢?通过参数池的方式,把所有这些参数统一放进一个列表std::vector<TArg*> arg_list。所以,接下来,我们来定义一个参数池类:

/* Define an argument pool for multi-type arguments storage */
class TArgsPool
{
public:
	TArgsPool()
	{
		arg_list.clear();
		b_new_list = true;
	}

	/* Destructor for TArgsPool, Free all the allocated memory and clear the list */
	~TArgsPool()
	{
		for(int i = 0; i < arg_list.size(); i++)
		{
			delete arg_list[i];
		}

		arg_list.clear();
	}

	/* The first time, when arg_list is empty, add_arg_to_pool would be called  */
	/* 将int型数据加入参数池 */
	void add_arg_to_pool(int iv)
	{
		TArg *pArg = new TArgInt(iv);
		arg_list.push_back(pArg);
	}
	
	/* 将double型数据加入参数池 */
	void add_arg_to_pool(double dv)
	{
		TArg *pArg = new TArgDouble(dv);
		arg_list.push_back(pArg);
	}
	
	/* 将string型数据加入参数池 */
	void add_arg_to_pool(std::string sv)
	{
		TArg *pArg = new TArgString(sv);
		arg_list.push_back(pArg);
	}
	
	/* 将bool型数据加入参数池 */
	void add_arg_to_pool(bool bv)
	{
		TArg *pArg = new TArgBool(bv);
		arg_list.push_back(pArg);
	}

	/* 对于一种参数类型组合,如果存在多次传递需求,例如http的多次请求,则每次只需要更新参数即可,无需反复删除和新增 */
	/* When the arg_list is completely generated, update_arg_to_pool should be called */
	void update_arg_to_pool(int iv, int idx)
	{
		if((idx < 0) || (idx >= arg_list.size()))
		{
			std::cout << "Error: Invalid argument list index!" << std::endl;
			exit(1);
		}
		std::cout << "Update int value to " << iv << ", at " << idx << std::endl;
		arg_list[idx]->set_value(iv);
	}

	void update_arg_to_pool(double dv, int idx)
	{
		if((idx < 0) || (idx >= arg_list.size()))
		{
			std::cout << "Error: Invalid argument list index!" << std::endl;
			exit(1);
		}
		std::cout << "Update double value to " << dv << ", at " << idx << std::endl;
		arg_list[idx]->set_value(dv);
	}

	
	void update_arg_to_pool(std::string sv, int idx)
	{
		if((idx < 0) || (idx >= arg_list.size()))
		{
			std::cout << "Error: Invalid argument list index!" << std::endl;
			exit(1);
		}
		std::cout << "Update string value to " << sv << ", at " << idx << std::endl;
		arg_list[idx]->set_value(sv);
	}
	
	void update_arg_to_pool(bool bv, int idx)
	{
		if((idx < 0) || (idx >= arg_list.size()))
		{
			std::cout << "Error: Invalid argument list index!" << std::endl;
			exit(1);
		}
		std::cout << "Update bool value to " << bv << ", at " << idx << std::endl;
		arg_list[idx]->set_value(bv);
	}

	/* Push all the arguments in arg_list to lua stack */
	int push_args_to_stack(lua_State *L)
	{
		int arg_num = (int)arg_list.size();
		for(int i = 0; i < arg_num; i++)
		{
			arg_list[i]->push_value(L);
		}
		return arg_num;
	}

	/* When the first time arguments were added, reset the new list flag to false */
	void reset_new_list_flag()
	{
		b_new_list = false;
	}
	
	/* Check if argument list is new */
	bool check_if_new_list()
	{
		return b_new_list;
	}

private:
	std::vector<TArg*> arg_list;
	bool b_new_list;
};

该类中,当第一次收集参数时,通过添加的方式将参数添加到数据池中,add_arg_to_pool()方法;第二次同样的参数到来时,则使用更新的方式,update_arg_to_pool()方法,避免不断地释放和分配内存。然后通过方法push_args_to_stack()将所有参数推入lua栈。

测试一下:

/* 初始化函数 */
lua_State* script_initialize(const char* script_name)
{
	lua_State *pLua = luaL_newstate(); //创建一个Lua虚拟机
	if(!pLua)
	{
		std::cout << "Failed to open Lua!" << std::endl;
		return NULL;
	}
	 
	luaL_openlibs(pLua);  //将标准类库加载到Lua虚拟机
 
	luaL_dofile(pLua, script_name);   //加载并运行lua文件

	return pLua;
}


int main()
{
	int test_int = 10;
	std::string test_str = "This is a test string.";
	std::string test_str1 = "Another test string.";
	double test_dbl = 3.1415926;
	bool test_bool = true;
	
	lua_State *pL = script_initialize("multi_args.lua");
	if(!pL)
	{
		std::cout << "Script initialized failed!" << std::endl;
		return -1;
	}
	
	lua_getglobal(pL, "print_args");  //获取lua函数print_args
	if(!lua_isfunction(pL, -1))
	{
		std::cout << "Get lua function failed!" << std::endl;
		exit(1);
	}
	
	/* 定义一个数据池对象 */
	TArgsPool data_pool;
	
	/* 测试添加数据功能 */
	data_pool.add_arg_to_pool(test_int);
	data_pool.add_arg_to_pool(test_str);
	data_pool.add_arg_to_pool(test_str1);
	data_pool.add_arg_to_pool(test_dbl);
	data_pool.add_arg_to_pool(test_bool);
	
	/* 将以上数据池的数据都推入lua栈 */
	int data_num = data_pool.push_args_to_stack(pL);

	/* 调用lua函数打印数据 */
	lua_pcall(pL, data_num, 0, 0);

	/* 清空lua栈 */
	while(lua_gettop(pL))
	{
		lua_pop(pL, 1); 
	}
	
	lua_getglobal(pL, "print_args");
	if(!lua_isfunction(pL, -1))
	{
		std::cout << "Get lua function failed!" << std::endl;
		exit(1);
	}
	
	/* 测试更新数据功能 */
	data_pool.update_arg_to_pool((int)100, 0);
	data_pool.update_arg_to_pool((std::string)"Hello", 1);
	data_pool.update_arg_to_pool((std::string)"Lua", 2);
	data_pool.update_arg_to_pool((double)9.12345, 3);
	data_pool.update_arg_to_pool((bool)false, 4);
	
	/* 将更新后的数据推入lua栈并调用lua打印函数 */
	data_num = data_pool.push_args_to_stack(pL);
	lua_pcall(pL, data_num, 0, 0);
	
	lua_close(pL);
	
	return 0;
}

Lua脚本:

function print_args(...)
	print("-----This is lua function-----")
	local args = {...}
	for k, v in pairs(args) do
		print(v)
	end
	return 0;
end

运行结果:

lua pairs输出顺序问题_参数传递