length of va_list when using variable list arguments?

后端 未结 6 454
花落未央
花落未央 2020-12-14 15:23

Is there any way to compute length of va_list? All examples I saw the number of variable parameters is given explicitly.

6条回答
  •  醉酒成梦
    2020-12-14 15:44

    One approach that hasn't been mentioned yet is to use a pre-processor macro to call the variadict function using the va_list length as the first parameter and also forward along the arguments. This is somewhat of a "cute" solution, but does not require manually inputting the argument list length.

    Assume you have the following function:

    int Min(int count, ...) {
        va_list args;
        va_start(args, count);
    
        int min = va_arg(args, int);
        for (int i = 0; i < count-1; ++i) {
          int next = va_arg(args, int);
          min = min < next ? min : next;
        }
        va_end(args);
    
        return min;
    }
    

    The idea is that you have a preprocessor macro capable of counting the number of arguments by using a mask for the __VA_ARGS__. There are a few good preprocessor libraries for determining the __VA_ARGS__ length including P99 and Boost Preprocessor, but just so I don't leave holes in this answer, here's how it can be done:

    #define IS_MSVC _MSC_VER && !__INTEL_COMPILER
    
    /**
     * Define the macros to determine variadic argument lengths up to 20 arguments. The MSVC 
     * preprocessor handles variadic arguments a bit differently than the GNU preprocessor,
     * so we account for that here. 
     */
    #if IS_MSVC
      #define MSVC_HACK(FUNC, ARGS) FUNC ARGS
      #define APPLY(FUNC, ...) MSVC_HACK(FUNC, (__VA_ARGS__))
      #define VA_LENGTH(...) APPLY(VA_LENGTH_, 0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
    #else
      #define VA_LENGTH(...) VA_LENGTH_(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
    #endif
    
    /**
     * Strip the processed arguments to a length variable.
     */
    #define VA_LENGTH_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
    

    Note: A lot of the noise from above is work-around support for MSVC.

    With the above defined, you can create a single macro to perform all length based operations:

    /**
     * Use the VA_LENGTH macro to determine the length of the variadict args to
     * pass in as the first parameter, and forward along the arguments after that.
     */
    #define ExecVF(Func, ...) Func(VA_LENGTH(__VA_ARGS__), __VA_ARGS__)
    

    This macro is capable of calling any variadict function as long as it begins with the int count parameter. In short, instead of using:

    int result = Min(5, 1, 2, 3, 4, 5);
    

    You can use:

    int result = ExecVF(Min, 1, 2, 3, 4, 5);
    

    Here's a template version of Min which uses the same approach: https://gist.github.com/mbolt35/4e60da5aaec94dcd39ca

提交回复
热议问题