DEV Community

Discussion on: How do I use variable-arguments (varargs) in C++ (effectively)?

Collapse
 
omercsp profile image
Omer • Edited

Let's break it down a bit, because you are mixing a couple of questions here (and ignoring the original one):

a. Regarding std::string usage in the printf functions family - All the stdio vararg functions (printf, sprintf, snprintf, etc) are old C functions. They have no knowledge (and can't have any) of C++ objects and such. This doesn't mean you can't use them in sprintf (or printf, or whatever). In particular std::string implements a nice function named c_str that returns a c-style string (i.e. const char*) of the string object. This is exactly what you need to for using it in and Xprintf function. For example, this snippet:

std::string s = "If I had wings, I wouldn't do anything beautiful and transcendent";
printf("This is my string - %s\n", s.c_str());
Enter fullscreen mode Exit fullscreen mode

Will work as you expect.

b. Now, the other question is - what should you use for string formatting in C++? Well, you can go either way, up to you and what your are trying to do:

  • First, never use sprintf. If you are going down the (valid and beautiful) road of 'using some C functions' always use snprintf to avoid overflows. As we saw above, it works.

  • Second, C++ STL has some nice facilities to give you the parse-things-into-a-buffer mechanism you need. In particular, the std::stringstream:

  std::stringstream sstr;
  std::string s = "No, I'd get my finger into everything I wanted";
  sstr << s << ", and here are some numbers " << 5 << " " << 7;
  std::cout <<sstr.str() << std::endl;
Enter fullscreen mode Exit fullscreen mode

Guess what. That would also work. It knows how to take pretty much everything C++ native, and if you know what you're doing (mostly, implementing << operators for your own types), you can extend it to take whatever you want.

Which one should you use? Well, up to you. Both are fine. It's frequently claimed the C variants are faster and easier to read and understand. The C++ way is more generic, and should probably be preferred if you need multiple format calls, as there's a object (the std::stringstream you would create) you can refer to. You better get yourself familiar with both of them and choose the right tool for what it is you are trying to do (always a good idea).

BTW, you do know can concatenate strings with +, right? If all you do is putting strings together, this will be the easiest way.

Please note that this is a very different question than what you had asked in the first place. From "what do to instead of varargs?", (Nothing. Use varargs) we moved to "Is there a C++ way to do what sprintf does?". When asking for help, be concise, and ask about the "real" problem, not about the layer(s) above it. The solution is usually there.

Thread Thread
 
baenencalin profile image
Calin Baenen

Please note that this is a very different question than what you had asked in the first place. From "what do to instead of varargs?", (Nothing. Use varargs) to "Is there a C++ way to do what sprintf does?". When asking for help, be concise, and ask about the "real" problem, not about the layer(s) above it. The solution is usually there.

I asked because the question was relevant to why I made my post, and because you said most of C's files are valid in C++.
This prompted me to ask because all I want string formatting, but it seems if I want anything done right in C++, I have to do it myself.

std::string s = "If I had wings, I wouldn't do anything beautiful and transcendent";
printf("This is my string - %s\n", s.c_str());

(Why does the above code work w/o std::? - Can C++ resolve identifiers if there aren't multiple w/ the same name in diff namespaces?)

I need to capture the string here, which I could technically do, since std::string(char const*) constructor exists.
But, there's a problem here, I one, need to provide my own memory for the C-string provided to the first argument (I don't know if this applies to the var-args), which means I can't do char* b = "Hello, %s!"; sprintf(/* ... */);.
On top of that, I also have my format string saved in an std::string, and as I've tested, passing in std::string.c_str() (char const*) to sprintf is apparently a no-no (invalid conversion from char* to const char*).

Second, C++ STL has some nice facilities to give you the parse-things-into-a-buffer mechanism you need. In particular, the std::stringstream:

  std::stringstream sstr;
  std::string s = "No, I'd get my finger into everything I wanted";
  sstr << s << ", and here are some numbers " << 5 << " " << 7;
  std::cout <<sstr.str() << std::endl;

I've seen (and used) this before. I don't like it, plus, it's absolutely NOT what I want.
I want to be able to pass a string with any string formatters into a function, ans pass in a variable number of arguments, and get a formatted string returned. With this method, I have a static amount of inputs for the format (basically, the opposite of a variable amount*).

You better get yourself familiar with both of them and choose the right tool for what it is you are trying to do (alway a good idea).

At this point, I might as well figure out how to make it myself (which I was, hence the question about varargs in C++).
Since apparently C++ is shit at providing actual format-strings.

But seriously. Neither of these tools are the ones for me.

BTW, you do know can concatinate strings with +, right? If all you do is puting strings together, this will be the easyest way.

I could. But adding strings is not the same as formatting.
And funnily enough, this is EVEN WORSE than stringstream, because there's only support for concatenating other strings to it, and not numbers.
But concatenation isn't what I want in the first place.

Thread Thread
 
omercsp profile image
Omer • Edited

I apologize for criticizing the way you ask questions, but it's important. Knowing how to ask the right questions is an important tool for a developer. The fact is - you asked a question, and that is what you got an answer for. Then you asked another question, and got an answer for that one too. Now , on the third iteration, you ask another different question (still not clear, though).

Dude, this is not how you get work done.

Regardless:

if I want anything done right in C++, I have to do it myself

and

At this point, I might as well figure out how to make it myself (which I was, hence the question about varargs in C++).
Since apparently C++ is shit at providing actual format-strings.

Usually wrong. For C++, as well of most of the mature languages. If a language provides you the facility to do something, you need a very very good reason to not use it for the same task. So far, no such reason was provided.

I need to capture the string here, which I could technically do, since std::string(char const*) constructor exists.
But, there's a problem here, I one, need to provide my own memory for the C-string provided to the first argument (I don't know if this applies to the var-args), which means I can't do char* b = "Hello, %s!"; sprintf(/* ... */);.
On top of that, I also have my format string saved in an std::string, and as I've tested, passing in std::string.c_str() (char const*) to sprintf is apparently a no-no (invalid conversion from char* to const char*).

It looks like you got the sprintf arguments somewhat mixed up. This is how sprintf is declared (cleaned up a bit for clarity):

int sprintf (char *buff, const char *format, ...);

  • 1st argument is the target buffer, which is required to be mutable. So you must pass it a char array. For this, argument, std::string will not work. You need a proper char based buffer.

  • The 2nd argument, the format, is const char*, which means a std::string::c_strwill be perfect for it. Hence, code like this:

    char buff[256];
    std::string fmt = "%s%d";
    std::sprintf(buff, fmt.c_str(), "abc", 5);
    std::cout << buff << std::endl;
Enter fullscreen mode Exit fullscreen mode

is fine.

This has nothing to do with var-args. It's just the way this function is defined.

[sprintf is used here but NEVER usesprintf. ALWAYS use snprintf)]

I want to be able to pass a string with any string formatters into a function, ans pass in a variable number of arguments, and get a formatted string returned. With this method, I have a static amount of inputs for the format (basically, the opposite of a variable amount*).

This is probably the closest you got to describe your real problem (though still pretty vague, some code will probably help). If I got it right this time, you can find some answers here: stackoverflow.com/questions/234216.... Note the C++11 solution in the first answer formats the string twice, so it's robust and correct, but not very efficient in terms runtime(not a problem unless you write time sensitive application).

You can see that answers given there (unless you use C++20), are around the concepts I layered out during this discussion. You can dislike it all you want, but it's like that for a reason. Reinventing the wheel will usually give you grief, not joy.

And regarding your side question:

(Why does the above code work w/o std::? - Can C++ resolve identifiers if there aren't multiple w/ the same name in diff namespaces?)

Roughly speaking, C++ is a superset of C, thus any valid C is valid C++ code. Since printf(...() (without the std:: prefix) is valid C, any C++ compiler should be able to build it. In reality, there are some corner cases C code fails a C++ build, but 99% of the time, it's fine. In gcc 10, for example, printf and its friends defined within the std namespace following using statements for them (using std::printf and so), so you get the printf in the global namespace for free.

Thread Thread
 
baenencalin profile image
Calin Baenen

I apologize for criticizing the way you ask questions, but it's important. Knowing how to ask the right questions is an important tool for a developer.

Eh, criticism isn't something I treat with negativity, thanks for pointing out the flaw I only partially recognize.
Sorry for the "question hopping".

I'd say I got a bit carried away with trying to find a "best" way of string-formatting, and as of C++20, I found it std::format -- That's essentially everything I want and all I need.

Thread Thread
 
baenencalin profile image
Calin Baenen

I'd say I got a bit carried away with trying to find a "best" way of string-formatting, and as of C++20, I found it std::format -- That's essentially everything I want and all I need.

I tried looking for how to format strings in C++ before, but this never really came up to surface (I use DuckDuckGo). All I get is stringstream and snprintf/printf solutions.