Topic: std::string and format specification fields


Author: 5045-2h8p@spamex.com (Alexander Nasonov)
Date: Mon, 8 Apr 2002 15:09:47 GMT
Raw View
"Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au> wrote in message news:<a8e2kq$hds$1@spacebar.ucc.usyd.edu.au>...
> I am aware of the advantages of using stream formatting but I really miss
> the simplicity of doing something like this:
>
> std::string s;
> s.format("%s %lf\n", "Here is the number: ", x);
>

This is hot topic of many C++ discussions (boost library is good
example). Recently I posted my solution for this problem to
comp.std.c++ and comp.lang.c++.moderated (topic: template varargs).
Here is the cut:
--- cut ---
I'm trying to solve this problem for the format function. I'm
implmeneting it as background task at
http://cpp-experiment.sourceforge.net (there is no release yet).
For users, it's look like this:
   string s = format("<1><2><3><4><5>", 1, '2', "three")(4, 5);
Basic idea is using chain of temporaries returned by format and
following overloaded call operators. Every temporary store pointers to
argument and pointers to write function. Last temporary in chain is
then converted into string.
--- cut ---

Alexander Nasonov

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au>
Date: Tue, 9 Apr 2002 00:15:49 GMT
Raw View
I'm convinced that your solution not only presents a nicer syntax but it is
also elegant! I like it!

Andrew

"Richard Smith" <richard@ex-parrot.com> wrote in message
news:1017917005.18155.0.nnrp-07.3e31d362@news.demon.co.uk...
>
> "Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au> wrote in
message
> news:a8e2kq$hds$1@spacebar.ucc.usyd.edu.au...
> > I am aware of the advantages of using stream formatting but I really
miss
> > the simplicity of doing something like this:
> >
> > std::string s;
> > s.format("%s %lf\n", "Here is the number: ", x);
>
> Write a helper class like this,
>
>     class make_string
>     {
>     public:
>       template <class T> make_string &operator<<(const T &t)
>         { os << t; return *this; }
>       operator std::string() const { return os.str(); }
>     private:
>       std::ostringstream os;
>     };
>
> and then you can do
>
>     std::string s = make_string() << "Here is the number: "
>                                   << double(x) << "\n";
>
>
> In my opinion, a much nicer syntax (although I expect others will
disagree).
>
> If you *really* want to go the non-type safe, format string way, you can
> probably write a function that does something like
>
>     // untested code
>     std::string make_format_string( const char *fmt, ... )
>     {
>        va_list ap;
>        va_start( ap, fmt );
>
>        // vsnprintf is a common but non-standard extension.
>        // I am assuming C99 behaviour when the first two args
>        // are zero: not all implementations have this behaviour.
>        size_t sz = vsnprintf( NULL, 0, fmt, ap );
>
>        // If your string implementation guarantees contiguity
>        // of storage, you can use that.  AFAIK, the standard
>        // doesn't guarantee this for strings but does for vectors
>        std::vector<char> v( sz+1, '\0' );
>
>        vsnprintf( &v[0], sz+1, fmt, ap );
>        va_end( ap );
>
>        return std::string( &v[0], sz );
>     }
>
>
> > Where the format specification uses the conventional printf/scanf format
> > specifiers.
> >
> > Why was this never implemented for strings?
> > Is there a way I can easily do this myself?
> >
> > I know that the facets time_get and time_put use specifiers for
formatting
> > like %A etc. but I cannot find something equivalent for what I want to
do.
>
> Well 22.1.1/3 has an example implementation of an operator<< for a
> user-defined Date class which uses the time_get locale facet.  This is
> probably worth a glance.  If you want to use format-string strftime-style
> printing, then you can probably write a wrapper like the one above that
> calls down to strftime which uses C-style locales to format the date/time.
> I've never really used C-style locales (as opposed to C++ ones), so I
can't
> tell you if and how these work.
>
> > I guess that I have been motivated by being able to do it under Visual C
> > through the use of CString e.g.:
> > CString temp;
> > std::string s;
> >
> > temp.Format("%s %lf\n", "Here is the number: ", x);
> > s = temp;
> >
> > But I want to get rid of using CString and do it directly.
> >
> > Any comments/advice or a solution would be welcome.
>
> I'd really recommend not using the format-string style formatting unless
it
> is in an inner loop where speed is absolutely critical.  In my experience
> this is rarely the case as usually displaying the string takes far longer
> than formatting it with either mechanism.
>
> --
> Richard Smith
>
>
> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
> [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]
>


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au>
Date: Wed, 3 Apr 2002 20:16:25 GMT
Raw View
I am aware of the advantages of using stream formatting but I really miss
the simplicity of doing something like this:

std::string s;
s.format("%s %lf\n", "Here is the number: ", x);

Where the format specification uses the conventional printf/scanf format
specifiers.

Why was this never implemented for strings?
Is there a way I can easily do this myself?

I know that the facets time_get and time_put use specifiers for formatting
like %A etc. but I cannot find something equivalent for what I want to do.

I guess that I have been motivated by being able to do it under Visual C
through the use of CString e.g.:
CString temp;
std::string s;

temp.Format("%s %lf\n", "Here is the number: ", x);
s = temp;

But I want to get rid of using CString and do it directly.

Any comments/advice or a solution would be welcome.

Thanks
   Andrew




---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: Wed, 3 Apr 2002 19:14:23 CST
Raw View
Andrew Maclean wrote:
>
> I am aware of the advantages of using stream formatting but I really miss
> the simplicity of doing something like this:
>
> std::string s;
> s.format("%s %lf\n", "Here is the number: ", x);
>
> Where the format specification uses the conventional printf/scanf format
> specifiers.

Personally, I consider the following to be simpler:

std::stringstream s;
s << "Here is the number: " << x << endl;

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Richard Smith" <richard@ex-parrot.com>
Date: Thu, 4 Apr 2002 16:14:28 GMT
Raw View
"Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au> wrote in message
news:a8e2kq$hds$1@spacebar.ucc.usyd.edu.au...
> I am aware of the advantages of using stream formatting but I really miss
> the simplicity of doing something like this:
>
> std::string s;
> s.format("%s %lf\n", "Here is the number: ", x);

Write a helper class like this,

    class make_string
    {
    public:
      template <class T> make_string &operator<<(const T &t)
        { os << t; return *this; }
      operator std::string() const { return os.str(); }
    private:
      std::ostringstream os;
    };

and then you can do

    std::string s = make_string() << "Here is the number: "
                                  << double(x) << "\n";


In my opinion, a much nicer syntax (although I expect others will disagree).

If you *really* want to go the non-type safe, format string way, you can
probably write a function that does something like

    // untested code
    std::string make_format_string( const char *fmt, ... )
    {
       va_list ap;
       va_start( ap, fmt );

       // vsnprintf is a common but non-standard extension.
       // I am assuming C99 behaviour when the first two args
       // are zero: not all implementations have this behaviour.
       size_t sz = vsnprintf( NULL, 0, fmt, ap );

       // If your string implementation guarantees contiguity
       // of storage, you can use that.  AFAIK, the standard
       // doesn't guarantee this for strings but does for vectors
       std::vector<char> v( sz+1, '\0' );

       vsnprintf( &v[0], sz+1, fmt, ap );
       va_end( ap );

       return std::string( &v[0], sz );
    }


> Where the format specification uses the conventional printf/scanf format
> specifiers.
>
> Why was this never implemented for strings?
> Is there a way I can easily do this myself?
>
> I know that the facets time_get and time_put use specifiers for formatting
> like %A etc. but I cannot find something equivalent for what I want to do.

Well 22.1.1/3 has an example implementation of an operator<< for a
user-defined Date class which uses the time_get locale facet.  This is
probably worth a glance.  If you want to use format-string strftime-style
printing, then you can probably write a wrapper like the one above that
calls down to strftime which uses C-style locales to format the date/time.
I've never really used C-style locales (as opposed to C++ ones), so I can't
tell you if and how these work.

> I guess that I have been motivated by being able to do it under Visual C
> through the use of CString e.g.:
> CString temp;
> std::string s;
>
> temp.Format("%s %lf\n", "Here is the number: ", x);
> s = temp;
>
> But I want to get rid of using CString and do it directly.
>
> Any comments/advice or a solution would be welcome.

I'd really recommend not using the format-string style formatting unless it
is in an inner loop where speed is absolutely critical.  In my experience
this is rarely the case as usually displaying the string takes far longer
than formatting it with either mechanism.

--
Richard Smith


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Giovanni Bajo" <giovannibajo@REMOVEliberoTHIS.it>
Date: Thu, 4 Apr 2002 16:20:20 GMT
Raw View
"Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au> ha scritto nel
messaggio news:a8e2kq$hds$1@spacebar.ucc.usyd.edu.au...
> I am aware of the advantages of using stream formatting but I really miss
> the simplicity of doing something like this:
>
> std::string s;
> s.format("%s %lf\n", "Here is the number: ", x);
>
> Where the format specification uses the conventional printf/scanf format
> specifiers.
>
> Why was this never implemented for strings?
> Is there a way I can easily do this myself?

Well, I think the old printf formatting is gone for good. I for once am not
using it any more, it's too much type unsafe and error-prone. I remember a
project (a little compiler) I was working on: half of the crash reports
turned out to be buggy sprintf() calls to format the error messages (%s used
on numbers, or stuff like that). That's something that simply should not
happen.
Using streams work quite well against those crashes, but it has another
cons: it splits the text into several little strings, and it quickly becomes
a nightmare for the translators (in case you need localization). For
example, if you incapsulate all your strings into a source file like text.c,
with printf()-style you have sentences like this:

char tx123[] = "In the basket brought by %s, there are %d apples, %d melons
and %d strawberries";

With streams, it becomes something like:

char tx123_1[] = "In the basket brought by ";
char tx123_2[] = ", there are ";
char tx123_3[] = " apples, ";
char tx123_4[] = " melons and ";
char tx123_5[] = " strawberries";

Which is much harder to translate, especially with respect to the
whitespaces near the quotes (that every translator on earth will remove a
lot of times by mistake).

The solution I prefer is the one used by Qt, which is indeed similar to
printf(), but typesafe:

QString myTest = QString("In the basket brought by %1, there are %2 apples,
%3 melons and %4
strawberries").arg(strName).arg(nApples).arg(nMelons).arg(nStrawberries);

Since it's not using ellipsis, the string class already knows the type of
the variables whose values must be insterted into the string, so you just
need to specify where you want them, using %1, %2, %3.  The text resource
file will look like:

char tx123[] = "In the basket brought by %1, there are %2 apples, %3 melons
and %4 strawberries";

Which is very easy. There is also another advantage: the translators can
rearrange the sentence at will, as required by a proper translation. For
example, in another language it may look like:

char tx123[] = "There are %2 apples, %3 melons and %4 strawberries in the
basket brought by %1".

The order of parameters has been changed, but the code will be still
working, since the first arg(strName) call will still modify %1, wherever in
the string it is.

Giovanni Bajo


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au>
Date: Sun, 7 Apr 2002 06:01:48 GMT
Raw View
Thanks for the idea.

Andrew


"Richard Smith" <richard@ex-parrot.com> wrote in message
news:1017917005.18155.0.nnrp-07.3e31d362@news.demon.co.uk...
>
> "Andrew Maclean" <a.maclean_remove_this_@acfr.usyd.edu.au> wrote in
message
> news:a8e2kq$hds$1@spacebar.ucc.usyd.edu.au...
> > I am aware of the advantages of using stream formatting but I really
miss
> > the simplicity of doing something like this:
> >
> > std::string s;
> > s.format("%s %lf\n", "Here is the number: ", x);
>
> Write a helper class like this,
>
>     class make_string
>     {
>     public:
>       template <class T> make_string &operator<<(const T &t)
>         { os << t; return *this; }
>       operator std::string() const { return os.str(); }
>     private:
>       std::ostringstream os;
>     };
>
> and then you can do
>
>     std::string s = make_string() << "Here is the number: "
>                                   << double(x) << "\n";
>
>
> In my opinion, a much nicer syntax (although I expect others will
disagree).
>
> If you *really* want to go the non-type safe, format string way, you can
> probably write a function that does something like
>
>     // untested code
>     std::string make_format_string( const char *fmt, ... )
>     {
>        va_list ap;
>        va_start( ap, fmt );
>
>        // vsnprintf is a common but non-standard extension.
>        // I am assuming C99 behaviour when the first two args
>        // are zero: not all implementations have this behaviour.
>        size_t sz = vsnprintf( NULL, 0, fmt, ap );
>
>        // If your string implementation guarantees contiguity
>        // of storage, you can use that.  AFAIK, the standard
>        // doesn't guarantee this for strings but does for vectors
>        std::vector<char> v( sz+1, '\0' );
>
>        vsnprintf( &v[0], sz+1, fmt, ap );
>        va_end( ap );
>
>        return std::string( &v[0], sz );
>     }
>
>
> > Where the format specification uses the conventional printf/scanf format

> > specifiers.
> >
> > Why was this never implemented for strings?
> > Is there a way I can easily do this myself?
> >
> > I know that the facets time_get and time_put use specifiers for
formatting
> > like %A etc. but I cannot find something equivalent for what I want to
do.
>
> Well 22.1.1/3 has an example implementation of an operator<< for a
> user-defined Date class which uses the time_get locale facet.  This is
> probably worth a glance.  If you want to use format-string strftime-style
> printing, then you can probably write a wrapper like the one above that
> calls down to strftime which uses C-style locales to format the date/time.
> I've never really used C-style locales (as opposed to C++ ones), so I
can't
> tell you if and how these work.
>
> > I guess that I have been motivated by being able to do it under Visual C
> > through the use of CString e.g.:
> > CString temp;
> > std::string s;
> >
> > temp.Format("%s %lf\n", "Here is the number: ", x);
> > s = temp;
> >
> > But I want to get rid of using CString and do it directly.
> >
> > Any comments/advice or a solution would be welcome.
>
> I'd really recommend not using the format-string style formatting unless
it
> is in an inner loop where speed is absolutely critical.  In my experience
> this is rarely the case as usually displaying the string takes far longer
> than formatting it with either mechanism.
>
> --
> Richard Smith
>
>
> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
> [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]
>


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]