I am learning C++. cout
is an instance of std::ostream
class.
How can I print a formatted string with it?
I can still use printf
, but I want to learn a proper C++ method which can take advantage of all C++ benefits. I think this should be possible with std::ostream
, but I can't find the proper way.
One, the printf (short for "print formatted") function, writes output to the computer monitor. The other, fprintf, writes output to a computer file. They work in almost exactly the same way, so learning how printf works will give you (almost) all the information you need to use fprintf.
std::cout handles all types for you, while printf requires specific syntax depending on an integer type (there are non-integer types, but the only non-integer type you will use in practice with printf is const char * (C string, can be obtained using to_c method of std::string )).
C++ printf is a formatting function that is used to print a string to stdout. The basic idea to call printf in C++ is to provide a string of characters that need to be printed as it is in the program. The printf in C++ also contains a format specifier that is replaced by the actual value during execution.
The only thing you can do with std::ostream
directly is the well known <<
-syntax:
int i = 0;
std::cout << "this is a number: " << i;
And there are various IO manipulators that can be used to influence the formatting, number of digits, etc. of integers, floating point numbers etc.
However, that is not the same as the formatted strings of printf
. C++11 does not include any facility that allows you to use string formatting in the same way as it is used with printf
(except printf
itself, which you can of course use in C++ if you want).
In terms of libraries that provide printf
-style functionality, there is boost::format
, which enables code such as this (copied from the synopsis):
std::cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
Also note that there is a proposal for inclusion of printf
-style formatting in a future version of the Standard. If this gets accepted, syntax such as the below may become available:
std::cout << std::putf("this is a number: %d\n",i);
In C++20 you'll be able to use std::format
for safe printf
-like formatting:
std::cout << std::format("The answer is {}.\n", 42);
In addition to that the {fmt} library, std::format
is based on, provides the print
function that combines formatting and output:
fmt::print("The answer is {}.\n", 42);
Disclaimer: I'm the author of {fmt} and C++20 std::format
.
This is an idiom I have gotten used to. Hopefully it helps:
// Hacky but idiomatic printf style syntax with c++ <<
#include <cstdlib> // for sprintf
char buf[1024]; sprintf(buf, "%d score and %d years ago", 4, 7);
cout << string(buf) <<endl;
&
I suggest using ostringstream instead of ostream see following example :
#include <vector>
#include <string>
#include <iostream>
#include "CppUnitTest.h"
#define _CRT_NO_VA_START_VALIDATION
std::string format(const std::string& format, ...)
{
va_list args;
va_start(args, format);
size_t len = std::vsnprintf(NULL, 0, format.c_str(), args);
va_end(args);
std::vector<char> vec(len + 1);
va_start(args, format);
std::vsnprintf(&vec[0], len + 1, format.c_str(), args);
va_end(args);
return &vec[0];
}
example usage:
std::ostringstream ss;
ss << format("%s => %d", "Version", Version) << std::endl;
Logger::WriteMessage(ss.str().c_str()); // write to unit test output
std::cout << ss.str() << std::endl; // write to standard output
To implement printf one could use c++11 template parameters:
#include <iostream>
#include <string>
inline std::ostream & mprintf(std::ostream & ostr, const char * fstr) throw()
{
return ostr << fstr;
}
template<typename T, typename... Args>
std::ostream & mprintf(std::ostream & ostr,
const char * fstr, const T & x) throw()
{
size_t i=0;
char c = fstr[0];
while (c != '%')
{
if(c == 0) return ostr; // string is finished
ostr << c;
c = fstr[++i];
};
c = fstr[++i];
ostr << x;
if(c==0) return ostr; //
// print the rest of the stirng
ostr << &fstr[++i];
return ostr;
}
template<typename T, typename... Args>
std::ostream & mprintf(std::ostream & ostr,
const char * fstr, const T & x, Args... args) throw()
{
size_t i=0;
char c = fstr[0];
while (c != '%')
{
if(c == 0) return ostr; // string is finished
ostr << c;
c = fstr[++i];
};
c = fstr[++i];
ostr << x;
if(c==0) return ostr; // string is finished
return mprintf(ostr, &fstr[++i], args...);
}
int main()
{
int c = 50*6;
double a = 34./67.;
std::string q = "Hello!";
// put only two arguments
// the symbol after % does not matter at all
mprintf(std::cout, "%f + %f = %a \n", c, a);
// print string object: for real printf one should write q.c_str()
mprintf(std::cout, "message: \"%s\". \n", q);
// the last argument will be ignored
mprintf(std::cout, "%z + %f\n", (long)a, 12, 544 );
}
Output
300 + 2 = %a
message: "Hello!".
2 + 12
This a very simple code and it can be improved.
1) The advantage is that it uses << to print objects to the stream, so you can put arbitrary arguments that can be output via <<.
2) It ignores the type of the argument in the formatted string: after % can stand arbitrary symbol even a space. The output stream decides how to print the corresponding object. It also compatible with printf.
3) A disadvantage is that it can not print the percent symbol '%', one need to slightly improve the code.
4) It can not print formatted numbers, like %4.5f
5) If the number of arguments is less than predicted by formatted string, then the function just print the rest of the string.
6) If the number of arguments is greater than predicted by formatted string, then the remained arguments are ignored
One can improve the code to make 2)-6) to fully mimic the printf behaviour. However, if you follow the rules of printf, then only 3) and 4) need essentially to be fixed.
I wrote independently but came up with answer similar to user3283405
My solution uses vasprintf() to acheive formatting, and uses operator overloading of << of std::ostream to free the memory in right place.
Usage:
std::cout << putf(const char *format, ...); //Same format as C printf(3)
Code:
#define _GNU_SOURCE
#include <cstdarg>
#include <iostream>
#include <cstdio>
struct putf_r{
char *s;
};
putf_r putf(const char *fmt, ...){
va_list ap;
va_start(ap, fmt);
putf_r a;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
vasprintf(&a.s, fmt, ap);
#pragma GCC diagnostic pop
va_end(ap);
return a;
}
std::ostream& operator<<(std::ostream& os, putf_r a){
os<<a.s;
free(a.s);
return os;
}
int main(){
std::cout << putf("%3d\n", 23) << putf("%a\n", 256.);
}
Note that compiler doesn't check format inside putf(), so compiler flag -Wformat-nonliteral will not warn for suspicious code in putf() and you need to care uncontrolled format string problem by yourself.
Detailed info can be found on GitHub
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With