How reliable are streams?

Started by
24 comments, last by Fruny 17 years, 9 months ago
I'm currently using streams to convert numbers to strings and vica versa. I'm parsing numbers from a file, so for that the istringstream >> integer operation is used. Is it defined in the C++ standard how exactly this operation is to happen? Can I be certain, that if I type 465624 in a file, that this stringstream will always convert it correctly to the integer 465624? I didn't find any description on how this conversion works on cppreference.com, so that's why I'd like to know how well defined this is. If it's not well defined, then maybe some other version from another compiler could break the parsing of my files, because I'm relying on this stringstream to do it correctly, instead of my own string to integer conversion I used in the past. Also, is it possible to overload the >> and << operators of streams for integers and floating point numbers to give more options? Because iomanip is actually pretty limited, you can only choose from 3 numberal systems, no roman and not even binary. [Edited by - Lode on July 6, 2006 4:34:17 AM]
Advertisement
er... Roman?!
Holy crap I started a blog - http://unobvious.typepad.com/
You can add your own manipulators if you like.

And basic conversions like string -> int are pretty well defined, and should work the same on all (standards-compliant) compilers. :)
Class templates num_put and num_get is used for the formatting, their exact behavior is described in 22.2.2. Basically they get information from the facet and the actual input/output is just described in terms of printf and scanf, for instance:
Quote:The representations at the end of stage 1 consists of the char's that would be printed by a call of printf(s, val) where s is the conversion specifier determined above.
I still didn't get convinced, especially when the behaviour of it can be determined by "locales".

I made something that I can use in my game engine instead of streams, it has less features, but it does everything needed for my game engine and I have more control and certainty over it. This way I have full control over every byte read from the script files it parses.

/*To be exactly certain about how numbers get converted into characters and vicaversa, independent of locales and so on, I write my own StringStreamimplementation. It can convert from and to std::strings, which is enough tosuit the needs of everything done by the printing and file parsing done in thisgame engine.It can both input and output, all you have to do is define << and >> operatorsthe same way as you would for std::streams.*/class StringStream{    std::string s; //the buffer    int floatLength; //length for conversion to string from floating point numbers (not really like "precision" but attempts to be like that)        public:    std::string str() { return s; }    StringStream(std::string v) { s = v; floatLength = 8; }    StringStream() { s = ""; floatLength = 8; }        void setLength(int floatLength) { this->floatLength = floatLength; }    int getLength() { return floatLength; }        StringStream& operator<<(StringStream& v)    {        s += v.s;        return *this;    }        StringStream& operator>>(StringStream& v)    {        v.s = s;        return *this;    }        template<typename T>    void toUint(T& i) //converts the string to the given unsigned integer type    {        i = 0;        for(unsigned p = 0; p < s.length(); p++)        if(s >= '0' && s <= '9') i = 10 * i + (s - '0');    }        template<typename T>    void toSint(T& i) //converts the string to the given signed integer type    {        toUint(i);        if(s.size() > 0 && s[0] == '-') i = -i;    }        template<typename T>    void toFloat(T& f)    {        f = 0;        T div = 1;        bool afterPoint = false;        for(unsigned p = 0; p < s.length(); p++)        if(s >= '0' && s <= '9')        {            if(!afterPoint) { f = 10 * f + (s - '0'); }            else            {                div /= 10;                f += div * (s - '0');            }        }        else if(s == '.') afterPoint = true;        if(s.size() > 0 && s[0] == '-') f = -f;    }        template<typename T>    void fromUint(T i) //set the string to given unsigned integer    {        s = "";        if(i == 0) s = "0";        else while(i > 0) { s = char((i % 10) + '0') + s; i /= 10; }    }        template<typename T>    void fromSint(T i) //set the string to given signed integer    {        if(i >= 0) fromUint(i);        else        {            fromUint(-i);            s = ("-" + s);        }    }        template<typename T>    int floatMod10(T f)    {        if(f < 0) f = -f;        if(f < 1) return 0;                T s = 10; //subtractor        while(f > s) s *= 10;        s /= 10; //the above subtractor was one too large                while(s > 100 / 2) //I use 50 instead of 10 to avoid unprecisions        {            while(f > 0) f -= s;            f += s;            s /= 10;        }                return int(f) % 10;    }            template<typename T>    void fromFloat(T f) //set the string to given float    {        s = "";        bool negative = false;        if(f < 0)        {            negative = true;            f = -f;        }        T fcopy = f;        if(fcopy >= 0.0 && fcopy < 1.0)        {            s += "0";        }        else while(fcopy >= 1.0)        {            int digit = floatMod10(fcopy);            s = char(digit + '0') + s;            fcopy /= 10.0;        }                int lengthleft = floatLength - s.size() - 1; //The -1 is because the point itself is also a character.        if(negative) lengthleft--;                fcopy = f;        if(lengthleft > 0)        {            s += ".";        }        while(lengthleft > 0)        {            fcopy *= 10.0;            int digit = floatMod10(fcopy);            s = s + char(digit + '0');            lengthleft--;        }        if(negative) s = "-" + s;    }        StringStream& operator>>(unsigned char& v) { toUint(v); return *this; }    StringStream& operator>>(unsigned short& v) { toUint(v); return *this; }    StringStream& operator>>(unsigned int& v) { toUint(v); return *this; }    StringStream& operator>>(unsigned long& v) { toUint(v); return *this; }    StringStream& operator>>(char& v) { toSint(v); return *this; }    StringStream& operator>>(short& v) { toSint(v); return *this; }    StringStream& operator>>(int& v) { toSint(v); return *this; }    StringStream& operator>>(long& v) { toSint(v); return *this; }    StringStream& operator>>(float& v) { toFloat(v); return *this; }    StringStream& operator>>(double& v) { toFloat(v); return *this; }    StringStream& operator<<(unsigned char& v) { fromUint(v); return *this; }    StringStream& operator<<(unsigned short& v) { fromUint(v); return *this; }    StringStream& operator<<(unsigned int& v) { fromUint(v); return *this; }    StringStream& operator<<(unsigned long& v) { fromUint(v); return *this; }    StringStream& operator<<(char& v) { fromSint(v); return *this; }    StringStream& operator<<(short& v) { fromSint(v); return *this; }    StringStream& operator<<(int& v) { fromSint(v); return *this; }    StringStream& operator<<(long& v) { fromSint(v); return *this; }    StringStream& operator<<(float& v) { fromFloat(v); return *this; }    StringStream& operator<<(double& v) { fromFloat(v); return *this; }        StringStream& operator<<(const unsigned char& v) { fromUint(v); return *this; }    StringStream& operator<<(const unsigned short& v) { fromUint(v); return *this; }    StringStream& operator<<(const unsigned int& v) { fromUint(v); return *this; }    StringStream& operator<<(const unsigned long& v) { fromUint(v); return *this; }    StringStream& operator<<(const char& v) { fromSint(v); return *this; }    StringStream& operator<<(const short& v) { fromSint(v); return *this; }    StringStream& operator<<(const int& v) { fromSint(v); return *this; }    StringStream& operator<<(const long& v) { fromSint(v); return *this; }    StringStream& operator<<(const float& v) { fromFloat(v); return *this; }    StringStream& operator<<(const double& v) { fromFloat(v); return *this; }        StringStream& operator<<(const char* v) { s += v; return *this; }    StringStream& operator<<(std::string& v) { s += v; return *this; }    StringStream& operator<<(const std::string& v) { s += v; return *this; }};//usage: std::string str = valtostr(25454.91654654f);template<typename T>std::string valtostr(const T& val){    StringStream sstream; //also works with std::ostringstream instead    sstream << val;    return sstream.str();}//usage: double val = strtoval<double>("465498.654");template<typename T>T strtoval(const std::string& s){    StringStream sstream(s); //also works with std::istringstream instead    T val;    sstream >> val;    return val;}//length is decimal precision of the floating point numbertemplate<typename T>std::string valtostr(const T& val, int length){    StringStream sstream; //also works with std::ostringstream instead    //sstream << std::setprecision(length) << val;    sstream.setLength(length); sstream << val;    return sstream.str();}


[Edited by - Lode on July 6, 2006 7:30:17 AM]
Quote:Can I be certain, that if I type 465624 in a file, that this stringstream will always convert it correctly to the integer 465624?


Yes.

Quote:I didn't find any description on how this conversion works on cppreference.com, so that's why I'd like to know how well defined this is.


Buy yourself a copy of the C++ Standard.

Quote:If it's not well defined, then maybe some other version from another compiler could break the parsing of my files


That would be moronic.

Quote:I still didn't get convinced, especially when the behaviour of it can be determined by "locales".


The Standard C locale is active by default. It is the same for everybody.

Quote:This way I have full control over every byte read from the script files it parses.


Famous last words.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
Quote:I didn't find any description on how this conversion works on cppreference.com, so that's why I'd like to know how well defined this is.


Buy yourself a copy of the C++ Standard.


What does it cost and where to buy it? Google search didn't give me much helpful information.
Quote:Original post by Lode
What does it cost and where to buy it? Google search didn't give me much helpful information.


Here or here are your best choices. Note that the C locale is defined by the C Standard.

Quote:Also, is it possible to overload the >> and << operators of streams for integers and floating point numbers to give more options?


No.

Quote:Because iomanip is actually pretty limited, you can only choose from 3 numberal systems, no roman and not even binary.


Create a Roman or a Binary num_get and num_put facets and imbue your stream with them. They aren't supported by streams because, let's face it, they are of little use. I can see no justification for having roman numbers in the standard library...

You can also read and write binary using a std::bitset or a boost::dynamic_bitset.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
Quote:Original post by Lode
What does it cost and where to buy it? Google search didn't give me much helpful information.


Here or here are your best choices. Note that the C locale is defined by the C Standard.


I know it's off topic, but, I'm wondering why someone would have to pay for the C++ standard. I thought C++ was a free and open language that wasn't owned by anyone. But to read the standard of it you have to pay money to ANSI. Does this money go to ANSI, or does it go to the designers of C++ like Soustroup?
For the C standard, you'll want either this or that, though keep in mind that most C compilers implement C89, rather than C99.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan

This topic is closed to new replies.

Advertisement