c++variadic-functions

va_list passed to a function using va_arg is not working


I've this code that plays with variadic functions:

#include <cstdarg>
#include <iostream>

template <typename T>
void bar(va_list vl) {
    std::cout << va_arg(vl, T) << std::endl;
}

void foo(int n, ...) {
    va_list vl;
    va_start(vl, n);
    while (n--) {
        bar<int>(vl);
    }
    va_end(vl);
}

int main() {
    foo(3, 1, 2, 3);
    return 0;
}

Unfortunately, the output of this code is platform dependent. In GCC 7 I get:

1
2
3

while in MSVC 2015 is

1
1
1

I'm tring to write code to get the GCC output, but I suppose I'm doing something wrong and I've no idea where. Which is the correct output, if any?

Both compilers are set at maximum warning level, but they are not printing any warning. In MSVC, being va_list an alias to char*, the output makes sense. In GCC it is a builtin type and I've no idea what happens there.

Edit:

If I change the definition of bar to void bar(va_list& vl) then the outputs are the same. Is it legal to pass a va_list by reference?


Solution

  • Your program invokes undefined behavior.

    The semantics of C standard library in C++ is the same as in C and the content of cstdarg should be the same as stdarg.h in C. C++ standard is big, so I'll use C standard here, which is easier for me to read. From C99 7.15.3, annotation mine:

    .... The object ap [of type va_list, my annotation] may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap, the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap.

    If you pass va_list object to another function and in that function you call va_arg, you have to call va_end in the same function.

    According to the footnote 221 from C99 you can pass a pointer to va_list.