Sala ensemble (prefast) to provide a number of variable arguments

I have a variational function:

print_n_integers(7, 1, 2, 3, 4, 5, 6, 7);

int print_n_integers( unsigned int count, ... )
{
    // use va_start etc.
}

      

I would like to use the Microsoft SAL annotations from sal.h

so that the Visual Studio compiler will notice when the number of parameters does not match count

. I can provide count

as a literal in this way:

int print_n_integers (
    _Literal_ unsigned int count,
    ...
)

      

and Visual Studio at least has some trickery to deal with printf()

, but is there something for simple parameter counting?

+3


source to share


2 answers


I would like to suggest an alternative approach to your problem that does not use static analysis.

Even if you can get the count of the variational arguments for static analysis, you still need to provide the correct count for each call. You should also make sure that all arguments are (or may be promoted) integers.

A more natural, nonvariant approach to handling a list of homogeneous types is to print an array:

    void print_nint(const int *arr, size_t n);

      

C99-compliant compilers can create arrays in-place with composite literals :

    print_nint((int[]) {0, 8, 15}, 3);

      

This syntax can be wrapped in a macro:

    #define print_int(...)                                  \
        print_nint((int[]){__VA_ARGS__},                    \
            sizeof((int[]) {__VA_ARGS__}) / sizeof(int))

      

This macro does the count, and you can use it without an explicit count argument:



    print_int(1, 2, 3.0, rand(), i++, j++);

      

The second expansion of variational arguments is used internally sizeof

and is not evaluated. The line above rand()

is called only once, and i

and are j

incremented only once. The floating point argument is converted to int

before printing.

Visual Studio introduced complex literals in 2013 RC , but older versions don't support them. Unfortunately, complex literals also make your code unusable for C ++ compilers. For these cases, you can rework the macro to define a temporary array:

    #define print_int(...) {                                            \
        int PrintMe_[] = {__VA_ARGS__};                                 \
        print_nint(PrintMe_, sizeof(PrintMe_) / sizeof(*PrintMe_));     \
    }

      

This strategy only works if the function is called in a void context.

If you only use a macro, the array and its size are consistent. But you can post the base implementation and annotate it with SAL:

    void print_nint(_In_reads_(n) const int *arr, size_t n);

      

I am not a fan of free use of macros, but in this case the macro acts as a template; it can reduce dangerous redundancy.

+2


source


Unfortunately, there are currently no comments in SAL for this case.



Support for the function classes printf

/ scanf

/ is scanf_s

hardcoded in the parsers and is available via the annotation _Printf_format_string_

/ _Scanf_format_string_

/ _Scanf_s_format_string_

, respectively (and for special cases, _Printf_format_string_params_

/ _Scanf_format_string_params_

/ _Scanf_s_format_string_params_

). So your only chance is to lobby the code review team to add support for your use case.

+1


source







All Articles