介绍

stdarg.h头文件中定义了一些用于在被调用函数中访问可变参数的宏,比如说获取指定可变参数的值、更新可变参数的值。

  1. va_start:用于初始化可变参数列表
// ap:待初始化的变量,用于存放可变参数列表
// paramN:函数定义中最后一个有名参数名
void va_start (va_list ap, paramN);
  1. va_arg:用于获取可变参数的值
// ap:使用va_start宏初始化好的变量
// type:待获取可变参数的数据类型
// 返回值:可变参数的值
type va_arg (va_list ap, type)
  1. va_end:在va_stat调用之后,需要调用这个。
// ap:使用va_start宏初始化好的变量
void va_end (va_list ap);

案例

  1. 示例:以指定的格式输出可变参数的值
#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;
}
  1. 示例:实现一个通用的调用函数,传入相应的参数就可以调用Lua中定义好的全局函数
    1. 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
    
    1. 实现的通用的调用函数如下:
    #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;
    }
    
    1. 使用g++ xxx.cpp -llua编译后运行结果如下:

参考:参考