printf这个使用频繁的C语言函数的参数列表包含一个const char*的描述串,还有一个可变长参数(...) ,如下为printf的函数声明。
int printf(const char * __restrict, ...)
在stdarg.h这个头文件中包含着对可变长参数进行操作的一些宏(x86平台为例):
#define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
其中运行va_start(ap, v)可以使得ap指向可变长参数列表的第一个参数的地址。其中v为最后一个固定参数。
va_arg(ap,T)使ap指向可变长参数列表的下一个参数,并且用输入的类型T进行数据强转换,返回一个用户需要的参数值。
va_end(ap),置ap为NULL。
详细的可变长参数解释请参见这个博客:imjacob的专栏——c语言中的printf实现
1 #include <stdarg.h> 2 3 void call_va(const char * func,const char *sig,...) 4 { 5 va_list vl;//定义一个可变长参数指针,无初始值 6 int narg;//可变长参数的数量 7 int nres;//Lua函数返回值的数量 8 9 va_start(vl,sig);//让指针指向可变长参数的第一个参数首地址 10 lua_getglobal(L,func);//函数入栈 11 12 //---参数入栈 13 for(narg=0;*sig;narg++)//遍历参数 14 { 15 //检查栈中空间 16 luaL_checkstack(L,1,"too many arguments"); 17 18 switch(*sig++) 19 { 20 //根据参数的约束符,按类型入栈,d-double i-int s-char* 21 case 'd': 22 lua_pushnumber(L,va_arg(vl,double)); 23 break; 24 case 'i': 25 lua_pushinteger(L,va_arg(vl,int)); 26 break; 27 case 's': 28 lua_pushstring(L,va_arg(vl,char*)); 29 break; 30 case '>': 31 //输入参数结束 32 goto endargs; 33 default: 34 error(L,"invalid option (%c)",*(sig-1)); 35 } 36 } 37 //---参数入栈 38 endargs: 39 nres=strlen(sig);//期望的Lua函数返回值数量 40 41 //调用Lua函数 42 if(lua_pcall(L,narg,nres,0)!=0) 43 { 44 error(L,"error calling '%s':%s",func,lua_tostring(L,-1)); 45 } 46 47 //调用之后的返回值检索 48 //栈索引从栈顶往下依次是-1,-2,-3,所以第一个结果的栈索引是-nres 49 nres=-nres; 50 while(*sig) 51 { 52 //依次得到返回值的类型描述符 *sig 53 switch(*sig++) 54 { 55 case 'd': 56 if(!lua_isnumber(L,nres)) 57 error(L,"wrong result type"); 58 *va_arg(vl,double*)=lua_tonumber(L,nres); 59 break; 60 61 case 'i': 62 if(!lua_isnumber(L,nres)) 63 error(L,"wrong result type"); 64 *va_arg(vl,int*)=lua_tointeger(L,nres); 65 break; 66 67 case 's': 68 if(!lua_isstring(L,nres)) 69 error(L,"wrong result type"); 70 *va_arg(vl,const char**)=lua_tostring(L,nres); 71 break; 72 73 default: 74 error(L,"invalid option (%c)",*(sig-1)); 75 } 76 //结果的参数索引+1 77 nres++; 78 } 79 va_end(vl); 80 81 }
这样,如果我们需要调用一个lua函数,只需要如下的调用形式:
call_va("func","dd>ds",x,y,&z,&s);
表示这个lua函数的函数名为func,接受两个double,返回一个double和一个字符串,'>'表示参数和返回值结果的分割符。