Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use C++ std::ostream with printf-like formatting?

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.

like image 547
eonil Avatar asked Feb 27 '13 07:02

eonil


People also ask

What is formatted output using printf () statement explain it?

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.

What is the difference between std :: cout and printf?

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

What does printf mean in C++?

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.


6 Answers

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);
like image 68
jogojapan Avatar answered Oct 19 '22 19:10

jogojapan


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.

like image 25
vitaut Avatar answered Oct 19 '22 19:10

vitaut


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;

&

like image 13
Anand Venkataraman Avatar answered Oct 19 '22 21:10

Anand Venkataraman


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
like image 11
serup Avatar answered Oct 19 '22 20:10

serup


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.

like image 5
Sergiy Maksymenko Avatar answered Oct 19 '22 20:10

Sergiy Maksymenko


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

like image 3
WingTillDie Avatar answered Oct 19 '22 21:10

WingTillDie