Search code examples
c++formatting

Why can't std::string be the last named argument before '...'?


I have a function formatToStr() which receives a format in const std::string& and returns the formatted string.

#include <string>
#include <cstdarg>

std::string formatToStr(const std::string& format, ...) {

        char buff[1024];
        va_list args;
        va_start(args, format.c_str());
        vsnprintf(buff, sizeof(buff), format.c_str(), args);
        va_end(args);
        return std::string(buff);
}

int main() {

        printf("Testing: %s\n", formatToStr(std::string("test")).c_str());
        return 0;
}

This code compiles with a warning:

$ g++ --version
g++ (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
...
$ g++ ./c.cpp 
./c.cpp: In function ‘std::string formatToStr(const string&, ...)’:
./c.cpp:8:9: warning: second parameter of ‘va_start’ not last named argument [-Wvarargs]
    8 |         va_start(args, format.c_str());
      |         ^~~~~~~~

When changing format type to const char* there's no warning.

Why is that?
Thanks.


Solution

  • You slightly misinterpret the error message. The culprit is not the last named argument but the second parameter: format.c_str() is not format. va_start is a macro, it needs the name of the last named argument.

    However, as mentioned in comments, va_start(args,format) would be wrong too, because you may not use a reference here, see https://timsong-cpp.github.io/cppwp/n4868/cstdarg.syn#1.sentence-4. It is undefined. Some compilers diagnose it (but others dont). You could use a string_view passed by value.

    Last but not least, va_args is basically outdated since C++11 introduced variadic templates. And for formatting of strings via %s-like formatters you should take a look at <format>.