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++?
Top comments (15)
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.
Do C++'s wrappers add support for C++ types?E.g. you could format instd::string
usingsprintf
in<cstdio>
?Just tested in SoloLearn's C++ code playground, turns out you can't.
Also, do things get placed into thestd
(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
-- forstd::string
s.)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 stdiovararg
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 particularstd::string
implements a nice function namedc_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: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
: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. Usevarargs
) 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.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.
(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 instd::string.c_str()
(char const*
) tosprintf
is apparently a no-no (invalid conversion from char* to const char*
).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*).
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.
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.
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:
and
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.
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: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 usesnprintf
)]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:
Roughly speaking, C++ is a superset of C, thus any valid C is valid C++ code. Since
printf(...()
(without thestd::
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 thestd
namespace followingusing
statements for them (using std::printf
and so), so you get theprintf
in the global namespace for free.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.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
andsnprintf
/printf
solutions.It's a little late, but. Thanks for the reply.
I'm glad to be informed some (if not most) of C's libraries are still good to use.
@omercsp Since I got tread-locked, I thought I'd leave my response to a good point you made in this reply.
Excluding
snprintf
, C++ only has one way of doing "formatting",stringstream
s.This solution works well with a finite number of arguments, but when you need an unpredictable or changing amount; it gets hard to manage.
One may decide to make multiple function overloads for them to format their string, but this bloats the code, and requiring lots of overloads will make things harder to manage.
A negligible nitpick is that it looks bulky. - I heard it described as verbose. But, it's not. It's just bulky.
snprintf
definitely is the way to go.My opinion is just that C++ swung and missed here.
Since C++20, you have std::format() en.cppreference.com/w/cpp/utility/...
You can also you the great
fmt
library fmt.dev/latest/index.htmlI tried. I don't.
G++ (11.1.0 (GCC)) on Arch Linux (x86_64) says the
format
header does not exist.I'm not aware of how to install the
format
header.en.cppreference.com/w/cpp/compiler... : it seems that no compiler has support for at the moment...
std::format()
is just a function that you have in thefmt
library. I believe everybody keeps using it ^^C++ has the concept of variadic templates, which are just like C's variadic functions, just less of a hassle.
Less... but it comes with its own caveat, in order to use it, you must have one parameter of generic-type
T
, before...Args
(if you want to do anything useful with the type info).Another let-down is that you can't pass
void
toT
(if you have a parameter that is of typeT
), which means you must have some extra bit of data OR have an overload for your function that only takes the parameters beforeT
.For my case of string formatting, this is redundant because I'd literally just be returning the same string passed in.
But over-all, yes. Variadic templates are something I adore.
And I wish Java had them (then maybe I would have stuck around for a little longer).Doesn't Vargs also need to have at least one argument before the parameter pack too?
Could you expand on your second point?