0

What is the best way (short, using standard libraries and easy to understand) to do this in c++:

std::string s = magic_command("%4.2f", 123.456f)
  • without length limitations (char s[1000] = ...)
  • where "%4.2f" is any c format string (which would be given to printf for example)

I am aware of the snprintf malloc combo suggested for pure c in

writing formatted data of unknown length to a string (C programming)

but is there a better, less verbose, way to do this with c++?

I am also aware of the std::ostringstream method suggested in

Convert float to std::string in C++

but I want to pass a c format string such as "%4.2f", and I could not find a way to do this with ostringstream.

Community
  • 1
  • 1
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 1
    [Boost format](http://stackoverflow.com/questions/7583976/sprintf-with-c-string-class/7584035#7584035) sounds like what you're after. – Flexo Aug 20 '12 at 14:36
  • I can recommend Boost.Format, especially because it's templated. If you, for whatever reason, don't want to use it, there's also Poco::PatternFormatter: http://www.appinf.com/docs/poco/Poco.PatternFormatter.html – Robin Aug 20 '12 at 15:43

5 Answers5

8

You can try Boost.Format:

std::string s = boost::str(boost::format("%4.2f") % 123.456f);

It's not included in the standard, but Boost is about as standard as a non-standard library can get.

  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Rostyslav Dzinko Aug 21 '12 at 08:37
5

I would use a std::stringstream (in combination with setprecision) instead and use .str() afterwards to get the std::string.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
5

C++ completely abandons the concept of format string, so there will be no standard way to do it. You could implement magic_command yourself using asprintf (its vasprintf variant actually), though.

Note that *asprintf are GNU/BSD extensions. As such, they don't exist on Windows. Also, this solution is not type-safe, and will only accept POD types (so no classes, structs or unions).

std::string magic_command(const std::string& format, ...)
{
    char* ptr;
    va_list args;
    va_start(args, format);
    vasprintf(&ptr, format.c_str(), args);
    va_end(args);

    std::unique_ptr<char, decltype(free)&> free_chars(ptr, free);
    return std::string(ptr);
}
zneak
  • 134,922
  • 42
  • 253
  • 328
1

If you literally want to use your syntax and forgo type safety, I'd write a little wrapper class that wraps snprintf. I'd make it start out with a local, automatic buffer of some small size (say 2048?) and call snprintf once. If it succeeds, return a std::string created from that buffer. If you overran, allocate a std::string of the correct size and repeat the snprintf.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

New answer 10 years later: C++20 std::format

Finally, this will be the superior choice once you can use it:

#include <format>
#include <string>

int main() {
    std::cout << std::format("{:4.3}\n{:4.3}\n", 3.1, 3.1415);
}

Expected output:

 3.1
3.14

This will therefore completely overcome the madness of modifying std::cout state.

The existing fmt library implements it for before it gets official support: https://github.com/fmtlib/fmt Install on Ubuntu 22.04:

sudo apt install libfmt-dev

Modify source to replace:

  • <format> with <fmt/core.h>
  • std::format to fmt::format

main.cpp

#include <iostream>

#include <fmt/core.h>

int main() {
    std::cout << fmt::format("{:4.3}\n{:4.3}\n", 3.1, 3.1415);
}

and compile and run with:

g++ -std=c++11 -o main.out main.cpp -lfmt
./main.out

Output:

 3.1
3.14

Old answer

All answers given are good since there seems to be no really standard way of doing what I want, I'll vote you all up, and accept my own sum up course of action:

  • if the string is short enough to estimate its size by hand, do it, multiply your estimative by 4, and allocate it statically.
  • if you can get away with stringstream + setprecision, do it since it is standard
  • if not, and you are willing to write and include a short helper function based on snprintf/check overflow/dynamic allocation, do it and put it into your project "utils" file
  • finnally consider which dependency is less restrictive for your project (maybe you are already using one of them):
    • if boost is less restrictive, use Boost.Format
    • if GNU/BSD extensions are less restrictive, use asprintf
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985