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

10

65

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.

Windbroken answered 27/2, 2013 at 7:7 Comment(3)
this page is not a comprehensive guide to stream formatting Output FormattingBackside
You shouldn't really need it because you can do things like cout << "my baby's age is " << 3 << endl; instead of printf("My baby's age is %u\n", 3);Pentose
cout << "my baby's age is " << 3 << endl; is not localizable; in non-English language you may have different words order. So, this syntax is not acceptable for mutilanguage applications. The only way to force this syntax to work is to make switch that will depend on language ID, and such solution is ugly. printf is much better, because translator can translate entire format string and change words order without source code modification on each uncommon language.Irritability
C
39

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);
Clank answered 27/2, 2013 at 7:13 Comment(3)
How about string generation? Neither no way to make string object with formatting?Windbroken
@Eonil Yes. First of all, you can use a std::ostringstream object in the same way as std::cout above. When you've filled it with content (using the <<-operator), you can use its .str() function to get the formatted string. And boost::format returns a string anyway. I didn't include this in the answer because your question is about std::cout specifically.Clank
Also see fmtlib/fmt for an alternative, powerful C++ format library.Sabella
S
69

In C++20 you can 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, and C++23 provide 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.

Stagehand answered 7/12, 2020 at 16:33 Comment(6)
Thank you for your work here. I was always baffled by the very concept of using left shift operators for IO. C has plenty of problems that C++ hoped to tackle, but they really threw the baby out with the bathwater when they ditched format strings entirely (yes, I know printf has always still been there, but the compiler itself judges you for using libc in C++).Bodice
@Bodice C++ still has formatting of strings, but not really "printf" style formatting. You use std::xxx values to specify width, output format (for numbers), significant digits, and decimal places, which is 90% of what printf would do. It's incredibly more wordy, but the idea was that the output stream would hold the "formatting state". Never really caught on as a "great idea" and stuff like std::format fills in the compatibility gaps, still, there is formatting pre C++20, just not the "form" many like.Xenophanes
Seems a shame that fmt::print() was not standardised. fmt is great: flexible, easy, safe and FAST (especially for things like floats, with now dragonbox(?) under the hood). And then we must std::cout << std::format(...); ?? I did a simple test and fmt::print(...) was 10-15% faster straight away (and there is fmt::print(std::FILE,..) for anything else). And that is with std::ios::sync_with_stdio(false); Any solutions?Richy
We are working on it, hopefully std::print will make it to C++23.Stagehand
Doesn't std::format_to(std::ostream_iterator<char>(std::cout), "format string...", ...) pretty much do the same thing as fmt::print(... ? Seems like the dynamic string creation is avoided this way right ?Malmo
Lorah, print has a number of advantages compared to writing to an ostream_iterator: it is atomic (no interleaving of output), better handles Unicode and is much faster (the iterator outputs each character individually).Stagehand
C
39

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);
Clank answered 27/2, 2013 at 7:13 Comment(3)
How about string generation? Neither no way to make string object with formatting?Windbroken
@Eonil Yes. First of all, you can use a std::ostringstream object in the same way as std::cout above. When you've filled it with content (using the <<-operator), you can use its .str() function to get the formatted string. And boost::format returns a string anyway. I didn't include this in the answer because your question is about std::cout specifically.Clank
Also see fmtlib/fmt for an alternative, powerful C++ format library.Sabella
J
16

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;

&

Jokjakarta answered 27/1, 2020 at 17:14 Comment(4)
For me this is the best. Familiarity to context, and simple change in my C code to become C++. It put to use the cstdlib for what it is intended for.Hazing
Ofc this won't overflow until after you release your code, right LOL So best use something safer like snprintf (with caveats: see https://mcmap.net/q/15780/-which-of-sprintf-snprintf-is-more-secure)Decrepitude
sprintf (or std::sprintf) function is found in <cstdio> not in <cstdlib>Prove
There's no need to cast to a string, you can print the buffer directly.Kimikokimitri
O
12

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
Orthoepy answered 5/9, 2018 at 11:33 Comment(3)
This is the best answer in my opinion. Using vsnprintf to get the "number of characters that would be written", but not allowing it to write anything is genius. I had one modification...I used a character array directly instead of a vector (char vec[len + 1];). Figured it was less overhead. Then returned the character array (return vec;).Mccallion
This isn't the best answer though. As @JoeDimig said, return &vec[0] (or the more sensible return {vec.data(), vec.size()}) will copy the whole string for no good reason.Bodice
plus one for best answer. It's not C++20-only, no Boost and no C-style-*printf()-in-the-middle.Platino
L
5

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.

Locale answered 2/12, 2017 at 22:49 Comment(0)
P
3

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

Participle answered 1/9, 2018 at 14:28 Comment(1)
GCC and probably clang have attributes to annotate printf like functionsQuadric
P
2

Field Width

Setting field width is very simple. For each variable, simply precede it with "setw(n)". Like this:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
  const int max = 12;
  const int width = 6;
  for(int row = 1; row <= max; row++) {
      for(int col = 1; col <= max; col++) {
          cout << setw(width) << row * col;
      }
      cout << endl;
  }
  return 0;
}

Notice how "setw(n)" controls the field width, so each number is printed inside a field that stays the same width regardless of the width of the number itself.

-- From "Programming/C++ tutorial" by P. Lutus.

Phenomenalism answered 23/5, 2017 at 18:26 Comment(1)
If this whole answer is taken from the tutorial, you should indicate that with quote formatting.Huelva
L
1

Sample output:

2017-12-20T16:24:47,604144+01:00 Hello, World!

Code (with put_printf usage demonstrated in put_timestamp):

#include <assert.h>
#include <chrono>
#include <iomanip>
#include <iostream>

class put_printf {
    static constexpr size_t failed = std::numeric_limits<size_t>::max(); // for any explicit error handling
    size_t stream_size; // excluding '\0'; on error set to 0 or to "failed"
    char buf_stack[2048+1]; // MAY be any size that fits on the stack (even 0), SHOULD be (just) large enough for most uses (including '\0')
    std::unique_ptr<char[]> buf_heap; // only used if the output doesn't fit in buf_stack
public:
    explicit put_printf(const char *format, ...)
            #if __GNUC__
            __attribute__ ((format (printf, 2, 3))) // most compelling reason for not using a variadic template; parameter 1 is implied "this"
            #endif
            {
        va_list args;
        va_start(args, format);
        const int res = vsnprintf(buf_stack, sizeof(buf_stack), format, args);
        va_end(args);
        if (res < 0) { // easily provoked, e.g., with "%02147483646i\n", i.e., more than INT_MAX-1 significant characters (only observed, no guarantee seen)
            stream_size = failed;
        } else if (res < sizeof(buf_stack)) { // preferred path
            stream_size = res;
        } else { // not artificially constrained
            try {
                const size_t buf_size = static_cast<size_t>(res) + 1; // avoids relying on "res < INT_MAX" (only observed, no guarantee seen)
                buf_heap.reset(new char[buf_size]); // observed to work even beyond INT_MAX=2^32-1 bytes
                va_start(args, format);
                if (vsnprintf(buf_heap.get(), buf_size, format, args) == res) stream_size = res;
                else stream_size = failed; // can't happen
                va_end(args);
            } catch (const std::bad_alloc&) { // insufficient free heap space (or an environment-specific constraint?)
                stream_size = failed;
            }
        }
    }
    friend std::ostream& operator<<(std::ostream& os, const put_printf& self) {
        if (self.stream_size == failed) {
            // (placeholder for any explicit error handling)
            return os;
        } else {
            // using write() rather than operator<<() to avoid a separate scan for '\0' or unintentional truncation at any internal '\0' character
            return os.write((self.buf_heap ? self.buf_heap.get() : self.buf_stack), self.stream_size);
        }
    }
};

class put_timestamp {
    const bool basic = false;
    const bool local = true;
public:
    friend std::ostream& operator<<(std::ostream& os, const put_timestamp& self) {
        const auto now = std::chrono::system_clock::now();
        const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
        struct tm tm; if ((self.local ? localtime_r(&now_time_t, &tm) : gmtime_r(&now_time_t, &tm)) == nullptr) return os; // TODO: explicit error handling?
        static_assert(4 <= sizeof(int), "");
        const int microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch() % std::chrono::seconds(1)).count();
        assert(0 <= microseconds && microseconds < 1000000); // TODO: (how) do we know?
        // TODO: doesn't "point" in "decimal_point()" imply "dot"/"full stop"/"period", unlike an obviously neutral term like "mark"/"separator"/"sign"?
        const char decimal_sign = std::use_facet<std::numpunct<char>>(os.getloc()).decimal_point() == '.' ? '.' : ','; // full stop accepted, comma preferred
        // TODO: all well and good for a locale-specific decimal sign, but couldn't the locale also upset microseconds formatting by grouping digits?
        os << std::put_time(&tm, self.basic ? "%Y%m%dT%H%M%S" : "%FT%T") << put_printf("%c%06i", decimal_sign, microseconds);
        if (! self.local) return os << "Z";
        const int tz_minutes = std::abs(static_cast<int>(tm.tm_gmtoff)) / 60;
        return os << put_printf(self.basic ? "%c%02i%02i" : "%c%02i:%02i", 0 <= tm.tm_gmtoff ? '+' : '-', tz_minutes / 60, tz_minutes % 60);
    }
};

int main() {
    // testing decimal sign
    ///std::cout.imbue(std::locale("en_GB"));
    ///std::cout.imbue(std::locale("fr_FR"));

    std::cout << put_timestamp() << " Hello, World!\n";
    #if 0
    typedef put_printf pf; // just to demo local abbreviation
    std::cout << "1: " << pf("%02147483646i\n"  , 1     ) << std::endl; // res < 0
    std::cout << "2: " << pf("%02147483643i%i\n", 1, 100) << std::endl; // res < 0
    std::cout << "3: " << pf("%02147483643i%i\n", 1,  10) << std::endl; // works
    std::cout << "4: " << pf("%02147483646i"    , 1     ) << std::endl; // works
    #endif
    return 0;
}

Comments about put_printf:

// Reasons for the name "put_printf" (and not "putf" after all):
// - put_printf is self-documenting, while using the naming pattern also seen in std::put_time;
// - it is not clear whether the proposed std::putf would support exactly the same format syntax;
// - it has a niche purpose, so a longer name is not an objection, and for frequent local uses
//     it is easy enough to declare an even shorter "typedef put_printf pf;" or so.
// Evaluation of delegating to vsnprintf() with intermediate buffer:
// (+) identical result without implementation and/or maintenance issues,
// (?) succeeds or fails as a whole, no output of successful prefix before point of failure
// (-) (total output size limited to INT_MAX-1)
// (-) overhead (TODO: optimal buf_stack size considering cache and VM page locality?)
// Error handling (an STL design problem?):
// - std::cout.setstate(std::ios_base::failbit) discards further std::cout output (stdout still works),
//     so, to be aware of an error in business logic yet keep on trucking in diagnostics,
//     should there be separate classes, or a possibility to plug in an error handler, or what?
// - should the basic or default error handling print a diagnostic message? throw an exception?
// TODO: could a function "int ostream_printf(std::ostream& os, const char *format, ...)"
//           first try to write directly into os.rdbuf() before using buf_stack and buf_heap,
//           and would that significantly improve performance or not?
Lethargy answered 20/12, 2017 at 15:12 Comment(1)
Your code is unreadable. Please abstract away your apparent allergy to whitespace and newlines.Huelva
C
1

It is my decision using format C# Console.WriteLine with numbered placeholders for C++. This format is used in language PPL: https://github.com/okogosov/PPL/blob/main/CppPrintArgs.zip

See Tutorial PPL and other: https://github.com/okogosov/PPL

Carlotacarlotta answered 15/2 at 9:0 Comment(0)
R
-1

When I need both the typesafety of cout and the quick and easy formatting of simple variables of printf(), I mix the two like this. This is an ugly fix, but it gets things done for me when I need to output things like "02/07/2014 10:05am" together with some more complex entities:

#include <stdio>
#include <stdarg>
#include <stdlib>
#include <iostream>

#pragma hdrstop

using namespace std;


char* print(char* fmt, ...)
{
    static char buffer[80] = "";

    va_list argptr;
    va_start(argptr,fmt);

    vsprintf(buffer, fmt, argptr);

    va_end(argptr);

    return buffer;
}

#pragma argsused
int main(int argc, char* argv[])
{

cout << print("\n%06d\n%6d\n%6d\n%010.3f",1,12,123,123.456);

system("PAUSE>NUL");

return 0;

}
Rozellarozelle answered 7/2, 2014 at 10:24 Comment(1)
At least use vsnprintf to avoid the most obvious kind of error: buffer overruns.Appanage

© 2022 - 2024 — McMap. All rights reserved.