Problem with one-liner string conversion

Discussion in 'C++' started by jl_post@hotmail.com, Jun 17, 2008.

  1. Guest

    Hi,

    A few months back I remember reading through C++ newsgroups trying
    to find a way to quickly convert a number to a C++ std::string. I
    often see code like:

    // Create a string that holds a number:
    int num = 7; // for example
    char tmp[24];
    sprintf(tmp, "%d", num);
    const std::string str = tmp;

    but I wanted to see if there was a better way. I generally would use
    this:

    // Create a string that holds a number:
    int num = 7;
    std::eek:stringstream outStream; // #include <sstream>
    outStream << num;
    const std::string str = outStream.str();

    and this works great, but just like the first example, it declares and
    uses a temporary variable/object. What I'd have liked to see was some
    sort of method in std::string that handled the conversion in one line,
    like this:

    // NOTE: not real code!
    int num = 7;
    const std::string str = std::string::number(num);

    While I didn't find what I was looking for, I did find an
    interesting post, where someone posted this nice, simple function:

    // Converts many things to a std::string:
    #include <sstream>

    template <typename T>
    std::string toStdString(const T &thing)
    {
    std::eek:stringstream os;
    os << thing;
    return os.str();
    }

    Using this function, I can now write code like this:

    int num = 7;
    const std::string str = toStdString(num);

    This code is very clear, but it also has another benefit: Not only
    can I easily convert integers and floats to a std::string, but I can
    convert pretty much anything that I can send to std::cout.

    But then I thought of something: Why even define a templated
    function? I could write code like this:

    int num = 7;
    std::string str = (std::eek:stringstream() << num).str();

    It may not be as elegant as the toStdString() solution, but at least
    it won't require me to define (or include a header file of) the
    toStdString() function in every program I write. In addition, there's
    no need to introduce a temporary variable to the current scope.

    However, when I tried to compile this code, I got the following
    error message:

    testfile.cpp: In function `int main(int, char**)':
    testfile.cpp:23: error: 'struct std::basic_ostream<char,
    std::char_traits<char> >' has no member named 'str'

    I can't figure out why I'm getting this error message. I mean, I'm
    declaring a std::eek:stringstream object, using the "<<" operator on it
    (which, I believe, returns the very same std::eek:stringstream object),
    and then I call the ::str() method on it. std::eek:stringstream HAS to
    have ::str() defined, otherwise the toStdString() function above
    wouldn't compile.

    What puzzles me is that I've successfully compiled similar things
    with std::strings, like this:

    // Prints "Hello, world!":
    const char* word1 = "Hello";
    const char* word2 = "world";
    printf("%s", (word1 + std::string(", ") + word2 + "!\n").c_str());

    and have had no problems with it. (Basically, word1+std::string(", ")
    +word2+"!\n" returns a std::string, and the ::c_str() method is called
    on it, passing its internal data to printf() to use before the
    std::string goes out of scope.)

    I would think that what I'm trying to do with std::eek:stringstream is
    really no different, so it puzzles me why (std::eek:stringstream() <<
    num).str() generates a compiler error. Does anybody know why this
    generates a compiler error?

    Thanks in advance for any help.

    -- Jean-Luc
    , Jun 17, 2008
    #1
    1. Advertising

  2. Kai-Uwe Bux Guest

    wrote:

    > Hi,
    >
    > A few months back I remember reading through C++ newsgroups trying
    > to find a way to quickly convert a number to a C++ std::string. I
    > often see code like:
    >
    > // Create a string that holds a number:
    > int num = 7; // for example
    > char tmp[24];
    > sprintf(tmp, "%d", num);
    > const std::string str = tmp;
    >
    > but I wanted to see if there was a better way. I generally would use
    > this:
    >
    > // Create a string that holds a number:
    > int num = 7;
    > std::eek:stringstream outStream; // #include <sstream>
    > outStream << num;
    > const std::string str = outStream.str();
    >
    > and this works great, but just like the first example, it declares and
    > uses a temporary variable/object. What I'd have liked to see was some
    > sort of method in std::string that handled the conversion in one line,
    > like this:
    >
    > // NOTE: not real code!
    > int num = 7;
    > const std::string str = std::string::number(num);
    >
    > While I didn't find what I was looking for, I did find an
    > interesting post, where someone posted this nice, simple function:
    >
    > // Converts many things to a std::string:
    > #include <sstream>
    >
    > template <typename T>
    > std::string toStdString(const T &thing)
    > {
    > std::eek:stringstream os;
    > os << thing;
    > return os.str();
    > }
    >
    > Using this function, I can now write code like this:
    >
    > int num = 7;
    > const std::string str = toStdString(num);
    >
    > This code is very clear, but it also has another benefit: Not only
    > can I easily convert integers and floats to a std::string, but I can
    > convert pretty much anything that I can send to std::cout.
    >
    > But then I thought of something: Why even define a templated
    > function? I could write code like this:
    >
    > int num = 7;
    > std::string str = (std::eek:stringstream() << num).str();
    >
    > It may not be as elegant as the toStdString() solution, but at least
    > it won't require me to define (or include a header file of) the
    > toStdString() function in every program I write. In addition, there's
    > no need to introduce a temporary variable to the current scope.
    >
    > However, when I tried to compile this code, I got the following
    > error message:
    >
    > testfile.cpp: In function `int main(int, char**)':
    > testfile.cpp:23: error: 'struct std::basic_ostream<char,
    > std::char_traits<char> >' has no member named 'str'


    Note that the operator<< you are invoking is inherited from the
    std::basic_ostream<char>. Its return type is std::basic_ostream&. At that
    point, all information about the underlying object being a string stream is
    lost. An std::basic_ostream<char> object does not have a str() member
    function. You need to cast back:

    std::string str =
    static_cast< std::eek:stringstream& >( std::eek:stringstream() << num ).str();


    [snip]


    BTW: you should just put toStdString into your library and use it. It is
    much better than writing a cryptic line every time you want to convert
    something. Also, you are creating a stringstream object in either case.
    Whether it is a named local variable of a temporary doesn't really matter
    all that much.


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Jun 17, 2008
    #2
    1. Advertising

  3. Kai-Uwe Bux Guest

    Kai-Uwe Bux wrote:

    > wrote:
    >
    >> Hi,


    [snip]
    >> int num = 7;
    >> std::string str = (std::eek:stringstream() << num).str();
    >>
    >> It may not be as elegant as the toStdString() solution, but at least
    >> it won't require me to define (or include a header file of) the
    >> toStdString() function in every program I write. In addition, there's
    >> no need to introduce a temporary variable to the current scope.
    >>
    >> However, when I tried to compile this code, I got the following
    >> error message:
    >>
    >> testfile.cpp: In function `int main(int, char**)':
    >> testfile.cpp:23: error: 'struct std::basic_ostream<char,
    >> std::char_traits<char> >' has no member named 'str'

    >
    > Note that the operator<< you are invoking is inherited from the
    > std::basic_ostream<char>. Its return type is std::basic_ostream&. At that
    > point, all information about the underlying object being a string stream
    > is lost. An std::basic_ostream<char> object does not have a str() member
    > function. You need to cast back:
    >
    > std::string str =
    > static_cast< std::eek:stringstream& >( std::eek:stringstream() << num ).str();
    >

    [snip]

    Ah, and just for kicks, now try:

    #include <sstream>
    #include <iostream>

    int main ( void ) {
    char const * message = "Hello world.\n";
    std::cout <<
    static_cast< std::eek:stringstream& >
    ( std::eek:stringstream() << message ).str();
    }

    and interpret the output.


    I re-iterate: go with the toStdString() solution.


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Jun 17, 2008
    #3
  4. Martin York Guest

    On Jun 17, 3:28 pm, Kai-Uwe Bux <> wrote:
    > Kai-Uwe Bux wrote:
    > > wrote:

    >
    > >> Hi,

    >
    > [snip]
    >
    > >>    int num = 7;
    > >>    std::string str = (std::eek:stringstream() << num).str();

    >
    > >> It may not be as elegant as the toStdString() solution, but at least
    > >> it won't require me to define (or include a header file of) the
    > >> toStdString() function in every program I write.  In addition, there's
    > >> no need to introduce a temporary variable to the current scope.

    >
    > >>    However, when I tried to compile this code, I got the following
    > >> error message:

    >
    > >> testfile.cpp: In function `int main(int, char**)':
    > >> testfile.cpp:23: error: 'struct std::basic_ostream<char,
    > >> std::char_traits<char> >' has no member named 'str'

    >
    > > Note that the operator<< you are invoking is inherited from the
    > > std::basic_ostream<char>. Its return type is std::basic_ostream&. At that
    > > point, all information about the underlying object being a string stream
    > > is lost. An std::basic_ostream<char> object does not have a str() member
    > > function. You need to cast back:

    >
    > >   std::string str =
    > >   static_cast< std::eek:stringstream& >( std::eek:stringstream() << num ).str();

    >
    > [snip]
    >
    > Ah, and just for kicks, now try:
    >
    > #include <sstream>
    > #include <iostream>
    >
    > int main ( void ) {
    >   char const * message = "Hello world.\n";
    >   std::cout <<
    >     static_cast< std::eek:stringstream& >
    >     ( std::eek:stringstream() << message ).str();
    >
    > }
    >
    > and interpret the output.
    >
    > I re-iterate: go with the toStdString() solution.
    >
    > Best
    >
    > Kai-Uwe Bux



    Or get a hold of the boost libraries (boost.org) and use its
    lexical_cast<>(). It not only converts from string to number and
    number to string. But from anything to anything. As long as you can
    use a stream to convert them. Even better boost is the semi-offical
    testing ground for stuff that will be considered for the next
    standard. So it is highly tested and widely addopted.

    #include <boost/lexical_cast.hpp>

    int main()
    {
    std::string val("567");
    int num = boost::lexical_cast<int>(val);
    }
    Martin York, Jun 18, 2008
    #4
  5. James Kanze Guest

    On Jun 18, 1:03 am, Martin York <> wrote:
    > On Jun 17, 3:28 pm, Kai-Uwe Bux <> wrote:
    > > Kai-Uwe Bux wrote:
    > > > wrote:

    > > [snip]


    > Or get a hold of the boost libraries (boost.org) and use its
    > lexical_cast<>(). It not only converts from string to number
    > and number to string. But from anything to anything.


    I'm not sure that I'd consider that an advantage. It doesn't
    make sense to convert anything to anything (and going through
    the textual representation, which boost::lexical_cast does, is
    not necessarily the correct way to convert x to y).

    Even in the case of conversion to string, I'm not sure just how
    often a generic solution is appropriate; you probably don't want
    to format a value representing an interest rate (percent) in the
    same way you'd format a value representing a monetary value.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 18, 2008
    #5
  6. Martin York Guest

    On Jun 18, 1:34 am, James Kanze <> wrote:
    > On Jun 18, 1:03 am, Martin York <> wrote:
    > > Or get a hold of the boost libraries (boost.org) and use its
    > > lexical_cast<>(). It not only converts from string to number
    > > and number to string. But from anything to anything.

    >
    > I'm not sure that I'd consider that an advantage.  It doesn't
    > make sense to convert anything to anything (and going through
    > the textual representation, which boost::lexical_cast does, is
    > not necessarily the correct way to convert x to y).


    Context is everything.
    I hope (and imagine) other readers did not read into my last reply
    that I was suggesting that lexical_cast<>() should be used to convert
    from a type to any other arbitory type. Where appropriate stream can
    be used (via lexical_cast<>()) in a type safe way to convert types
    appropriately (Its usage will ALWAYS depend on the context of the
    problem). 'Normally' this will be <Type> => String or String =>
    <Type>. But as with all rules and complex systems it usage is not
    limited to just that.

    Martin
    Martin York, Jun 18, 2008
    #6
  7. Guest

    Thank you all for all your help. I really appreciate it.

    But before I end this post, let me answer a question some have been
    asking:

    On Jun 17, 3:21 pm, Paavo Helde <> wrote:
    >
    > What's wrong with the helper function approach?


    Nothing, really. I just wanted to know why the line:

    std::string str = (std::eek:stringstream() << num).str();

    wouldn't compile. That line to me is fairly simple (even thought some
    might call it "ugly"), that if I were to find a just-as-simple
    approach, I'd use that.

    However, I agree that the fix that uses the static_cast:

    std::string str =
    static_cast<std::eek:stringstream&>
    (std::eek:stringstream() << num).str();

    is not that "pretty" and that there are more readable ways to do the
    same thing.

    (Basically, I wanted to know why the code wouldn't compile. I
    didn't mean to imply that I rejected the helper-function approach.
    Sorry for the confusion.)

    Again, thanks for all your help.

    -- Jean-Luc
    , Jun 18, 2008
    #7
  8. James Kanze Guest

    On Jun 18, 8:22 pm, Martin York <> wrote:
    > On Jun 18, 1:34 am, James Kanze <> wrote:


    > > On Jun 18, 1:03 am, Martin York <> wrote:
    > > > Or get a hold of the boost libraries (boost.org) and use
    > > > its lexical_cast<>(). It not only converts from string to
    > > > number and number to string. But from anything to
    > > > anything.


    > > I'm not sure that I'd consider that an advantage. It
    > > doesn't make sense to convert anything to anything (and
    > > going through the textual representation, which
    > > boost::lexical_cast does, is not necessarily the correct way
    > > to convert x to y).


    > Context is everything.
    > I hope (and imagine) other readers did not read into my last
    > reply that I was suggesting that lexical_cast<>() should be
    > used to convert from a type to any other arbitory type. Where
    > appropriate stream can be used (via lexical_cast<>()) in a
    > type safe way to convert types appropriately (Its usage will
    > ALWAYS depend on the context of the problem). 'Normally' this
    > will be <Type> => String or String => <Type>. But as with all
    > rules and complex systems it usage is not limited to just
    > that.


    Yes. The problem with lexical_cast is that it is too
    convenient; it sort of works in a large number of cases where it
    probably shouldn't (and then fails for some particular values
    which you forgot to test).

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 18, 2008
    #8
  9. On Jun 18, 9:50 pm, wrote:
    >    std::string str = (std::eek:stringstream() << num).str();
    >
    > wouldn't compile.  That line to me is fairly simple (even thought some
    > might call it "ugly"), that if I were to find a just-as-simple
    > approach, I'd use that.


    Using a utility class, a string builder, you can achieve this terse
    solution:

    string s = SB << num;

    See here for discussion, description and limitations:

    http://groups.google.com/group/comp.lang.c .moderated/msg/a400421feca71ea1

    Regards,
    Vidar Hasfjord
    Vidar Hasfjord, Jun 19, 2008
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Mantorok Redgormor

    bitwise increment one liner

    Mantorok Redgormor, Dec 17, 2003, in forum: C Programming
    Replies:
    8
    Views:
    800
    Peter Shaggy Haywood
    Dec 19, 2003
  2. =?ISO-8859-1?Q?Morris_Carr=E9?=

    Python passes the Turing test with a one-liner !

    =?ISO-8859-1?Q?Morris_Carr=E9?=, Apr 1, 2004, in forum: Python
    Replies:
    1
    Views:
    326
    Jon Perez
    Apr 2, 2004
  3. Paul Watson

    Using for in one-liner

    Paul Watson, Aug 15, 2005, in forum: Python
    Replies:
    8
    Views:
    585
    Reinhold Birkenfeld
    Aug 17, 2005
  4. Richard Gration
    Replies:
    9
    Views:
    94
    Anno Siegel
    Oct 15, 2004
  5. Larry
    Replies:
    1
    Views:
    92
    Martien Verbruggen
    Feb 3, 2005
Loading...

Share This Page