How do I use variable-arguments (varargs) in C++ (effectively)?
source link: https://dev.to/baenencalin/how-do-i-use-variable-arguments-varargs-in-c-effectively-4gob
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Posted on Dec 4
How do I use variable-arguments (varargs) in C++ (effectively)?
So, C++ extends C, and as that being the case, the implementation of varargs persisted (and hasn't changed, since it could break already existing code).
In C, you could do #include <stdarg.h>
, which included some functions for navigating the arguments.
But now most of C's original .h
files are deprecated (in C++).
So I'm wondering, what's the effective way of using varargs in C++?
Discussion (7)
Collapse
Expand
Err... why do you think "most of C's original .h files are deprecated"? Many C++ application are using very old C functions and macros. Sometimes the C++ features (e.g. STL) provide better alternatives. and sometimes... well, sometimes it's just better to use old axes that are sharp enough.
In general, you can include "stdarg.h" as you used to in C (like any other C header), and most of the time it will work. The better and recommended approach when it comes to standard C headers, is to include the C++ wrappers for them so everything is compatible with C++. These headers are named "cHEADER" instead of "HEADER.h". For example instead of including <stdlib.h> you would include <cstdlib>. The same rule applies for "stdarg.h". Include <cstdarg> (note: no ".h" suffix!), and go on with you life.
Collapse
Expand
Do C++'s wrappers add support for C++ types?
E.g. you could format in std::string
using sprintf
in <cstdio>
?
Just tested in SoloLearn's C++ code playground, turns out you can't.
Also, do things get placed into the std
(or any other) namespace (as to not pollute the global NS)?
Yes.
(Because funnily enough, string formatting is the whole reason I'm here. - Trying to make my own sprintf
-- for std::string
s.)
Collapse
Expand
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 usesnprintf
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
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
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
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 dochar* b = "Hello, %s!"; sprintf(/* ... */);
.
On top of that, I also have my format string saved in anstd::string
, and as I've tested, passing instd::string.c_str()
(char const*
) tosprintf
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 astd::string::c_str
will 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
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
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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK