介绍
在stdarg.h
头文件中定义了一些用于在被调用函数中访问可变参数的宏,比如说获取指定可变参数的值、更新可变参数的值。
- va_start:用于初始化可变参数列表
// ap:待初始化的变量,用于存放可变参数列表
// paramN:函数定义中最后一个有名参数名
void va_start (va_list ap, paramN);
- va_arg:用于获取可变参数的值
// ap:使用va_start宏初始化好的变量
// type:待获取可变参数的数据类型
// 返回值:可变参数的值
type va_arg (va_list ap, type)
- va_end:在va_stat调用之后,需要调用这个。
// ap:使用va_start宏初始化好的变量
void va_end (va_list ap);
案例
- 示例:以指定的格式输出可变参数的值
#include <iostream>
#include <cstdarg>
void func(int a, const char* fmt, ...) {
va_list argp;
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
}
int main() {
// 200,success,12.35
func(100, "%d,%s,%.2f", 200, "success", 12.345);
return 0;
}
- 示例:实现一个通用的调用函数,传入相应的参数就可以调用Lua中定义好的全局函数
- func.lua中定义的全局函数如下:
function add(a, b) return a + b; end function concat(str1, str2) return str1 .. str2; end function fun(a, s) return a, s; end
- 实现的通用的调用函数如下:
#include <cstdarg> #include <string.h> extern "C" { #include "lua.h" #include "lauxlib.h" } void error(lua_State* L, const char* fmt, ...) { va_list argp; va_start(argp, fmt); vfprintf(stderr, fmt, argp); va_end(argp); } /** * @param L lua_State * @param func Lua中定义的全局函数名称 * @param sig 一个描述参数类型和结果类型的字符串,例如"dd>d"表示两个类型为double参数和一个double类型的返回值 * @param ... 参数列表以及存放结果的一组指向变量的指针 */ void call_va(lua_State* L, const char* func, const char* sig, ...) { va_list argp; int narg, nres; // 参数和结果的个数 va_start(argp, sig); // 函数压栈 lua_getglobal(L, func); // 函数参数压栈 for (narg = 0; *sig; narg++) { luaL_checkstack(L, 1, "too many arguments"); // 检查栈空间 switch (*sig++) { case 'd': // double类型 lua_pushnumber(L, va_arg(argp, double)); break; case 'i': // int类型 lua_pushinteger(L, va_arg(argp, int)); break; case 's': // string类型的参数 lua_pushstring(L, va_arg(argp, const char*)); break; case '>': // 参数结束 goto endArgs; default: error(L, "invalid option (%c)", *(sig - 1)); break; } } endArgs: nres = strlen(sig); // 期望的结果数,注意sig的值在上面的switch语句中已经更新 // lua_pcall成功调用以后需要nres个结果入栈 if (lua_pcall(L, narg, nres, 0) != LUA_OK) { error(L, "error calling '%s':%s", func, lua_tostring(L, -1)); } nres = -nres; // 第一个结果的栈索引 while (*sig) { switch (*sig++) { case 'd': { int isnum; double n = lua_tonumberx(L, nres, &isnum); if (!isnum) { error(L, "wrong result type"); } *va_arg(argp, double*) = n; break; } case 'i': { int isnum; int n = lua_tointegerx(L, nres, &isnum); if (!isnum) { error(L, "wrong result type"); } *va_arg(argp, int*) = n; break; } case 's': { const char* s = lua_tostring(L, nres); if (s == nullptr) { error(L, "wrong result type"); } *va_arg(argp, const char**) = s; break; } default: { error(L, "invalid option (%c)", *(sig - 1)); break; } } nres++; } va_end(argp); } int main() { lua_State* L = luaL_newstate(); if (luaL_dofile(L, "func.lua")) { error(L, "execute function 'luaL_dofile' failed:%s", lua_tostring(L, -1)); } // 调用Lua中的add函数 int ret; call_va(L, "add", "ii>i", 100, 200, &ret); printf("add():%d\n", ret); // 调用Lua中的concat函数 const char* str = nullptr; call_va(L, "concat", "ss>s", "lua", " an scripting language", &str); printf("concat():%s\n", str); // 调用Lua中的fun函数 call_va(L, "fun", "is>is", 1024, "I lova lua", &ret, &str); printf("fun():%d,%s\n", ret, str); // 调用方使用完函数运行的结果自行将栈中的返回值出栈 lua_close(L); return 0; }
- 使用
g++ xxx.cpp -llua
编译后运行结果如下:
参考:参考