5

I have a question very similar to std::string formatting like sprintf or this question but these are very old so I dare to retry in order to collect some approaches.

I want to have a function/method which takes arguments which define a formatted string with some variables, much like printf/sprintf. For example it could be a method send_string(ARGS) which would format and send a string to some receiver. E.g.:

server->send_message("value1: %d, value2: %0.3f, value3: %s", 2, 3.14, "hello world");

I know the stream concept and I know boost::format. But I wonder what's the least verbose approach without unnecessary dependencies.

Here is what comes to my mind:

  • use a stream based approach - result would look like:

    server->msg_out() << "value1: " << 2 
                      << ", value2: " << 3.14 
                      << ", value3: '" << "hello world" << "'";
    

    This look most plausible to me but it's very verbose, it's hard to see what the output would look like. Particularly when you try to try to achieve tabular output or special formatting for real values. On the plus side you don't have any dependencies.

  • Use C based variable arguments. Would look like the example above which is well known and used a lot even though it's deprecated, error prone and inflexible.

  • Use Boost.Format

    server->send_message(boost::format(
                "value1: %d, value2: %0.3f, value3: %s") % 2 % 3.14 % "hello world"));
    

    or (to avoid dependencies in your API):

    server->send_message(
        boost::str(
            boost::format(
                "value1: %d, value2: %0.3f, value3: %s") % 2 % 3.14 % "hello world")));
    

    which seems to be the most sprintf-like C++ based approach. But I don't like this approach neither - it's very slow, it has a cruel syntax, it's very verbose, too and it introduces the dependency to Boost (if you don't use it already).

So currently I would decide in favor of the stream based approach but every time I use it I think to myself: there must be something more elegant!

Are there simpler approaches I didn't think of yet?

Community
  • 1
  • 1
frans
  • 8,868
  • 11
  • 58
  • 132
  • I think format specifiers in C++ are just unnecessary and bad. Look at how `std::cout` does it. Use templates. C++ is advanced enough to avoid type-unsafe stuff like this. – cadaniluk Jan 02 '16 at 17:00
  • 2
    I think [this](http://stackoverflow.com/questions/15106102/how-to-use-c-stdostream-with-printf-like-formatting) is a slightly newer version of those old duplicates. It mentions [N3716](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3716.html), a proposal for the inclusion of a new `std::putf` function in the standard; but I don't think that proposal has been accepted into any future standard revision yet. – jogojapan Jan 02 '16 at 17:04
  • 1
    @cad The problem with io manipulators for formatting is that these don't fit well for internationalisation of output formats. You know, different formatting specifications may be needed, because words change their positions in different languages. – πάντα ῥεῖ Jan 02 '16 at 17:05
  • + it seems to be not trivial to implement a class which can be used for s.th. like `server->msg_out() << "my key: " << std::hex << key << std::endl`. Do you have a working example? – frans Jan 02 '16 at 17:40
  • Frankly, I would just accept an `std::string`. Then the user can assemble however he wants. – Baum mit Augen Jan 02 '16 at 18:09

1 Answers1

3

You might perhaps use, or take inspiration from, Qt QString::arg functions; it has a format string with %1 ... %9 but the arguments are given thru arg overloaded functions, e.g.

QString str;
long num;
double dbl;
QString outstr = 
 QString("str=%1 num=%2 dbl=%3").arg(str).arg(num).arg(dbl);

So the typing is by overloading arg but the format string is much more localization friendly.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • When you already use Qt this is an option. The resulting command in my example would be `server->send_message(QString("value1: %d, value2: %0.3f, value3: '%s'").arg(2).arg(3.14).arg("hello world"));` which is acceptably verbose but of course you need to *qtify* your API.. – frans Jan 02 '16 at 17:12
  • As I said, you could also take inspiration from that approach – Basile Starynkevitch Jan 02 '16 at 17:13