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

Time:12-09

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.

CodePudding user response:

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>.

  • Related